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 #include <utility> | 19 #include <utility> |
20 | 20 |
21 #include "base/base64.h" | 21 #include "base/base64.h" |
22 #include "base/json/json_reader.h" | |
23 #include "base/json/json_writer.h" | |
24 #include "base/logging.h" | 22 #include "base/logging.h" |
25 #include "base/memory/scoped_ptr.h" | 23 #include "base/memory/scoped_ptr.h" |
26 #include "base/metrics/histogram.h" | 24 #include "base/metrics/histogram.h" |
27 #include "base/sha1.h" | 25 #include "base/sha1.h" |
28 #include "base/string_number_conversions.h" | 26 #include "base/string_number_conversions.h" |
29 #include "base/string_tokenizer.h" | 27 #include "base/string_tokenizer.h" |
30 #include "base/string_util.h" | 28 #include "base/string_util.h" |
31 #include "base/time.h" | 29 #include "base/time.h" |
32 #include "base/utf_string_conversions.h" | 30 #include "base/utf_string_conversions.h" |
33 #include "base/values.h" | 31 #include "base/values.h" |
34 #include "crypto/sha2.h" | 32 #include "crypto/sha2.h" |
35 #include "googleurl/src/gurl.h" | 33 #include "googleurl/src/gurl.h" |
36 #include "net/base/asn1_util.h" | |
37 #include "net/base/dns_util.h" | 34 #include "net/base/dns_util.h" |
38 #include "net/base/ssl_info.h" | 35 #include "net/base/ssl_info.h" |
39 #include "net/base/x509_certificate.h" | 36 #include "net/base/x509_certificate.h" |
40 #include "net/http/http_util.h" | 37 #include "net/http/http_util.h" |
41 | 38 |
42 #if defined(USE_OPENSSL) | 39 #if defined(USE_OPENSSL) |
43 #include "crypto/openssl_util.h" | 40 #include "crypto/openssl_util.h" |
44 #endif | 41 #endif |
45 | 42 |
46 namespace net { | 43 namespace net { |
47 | 44 |
48 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year | 45 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year |
49 | 46 |
50 TransportSecurityState::TransportSecurityState(const std::string& hsts_hosts) | |
51 : delegate_(NULL) { | |
52 if (!hsts_hosts.empty()) { | |
53 bool dirty; | |
54 Deserialise(hsts_hosts, &dirty, &forced_hosts_); | |
55 } | |
56 } | |
57 | |
58 static std::string HashHost(const std::string& canonicalized_host) { | 47 static std::string HashHost(const std::string& canonicalized_host) { |
59 char hashed[crypto::kSHA256Length]; | 48 char hashed[crypto::kSHA256Length]; |
60 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | 49 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); |
61 return std::string(hashed, sizeof(hashed)); | 50 return std::string(hashed, sizeof(hashed)); |
62 } | 51 } |
63 | 52 |
53 TransportSecurityState::TransportSecurityState() | |
54 : delegate_(NULL) { } | |
Ryan Sleevi
2012/04/26 19:21:12
nit: move } to a new line
palmer
2012/04/27 23:52:34
Done.
| |
55 | |
64 void TransportSecurityState::SetDelegate( | 56 void TransportSecurityState::SetDelegate( |
65 TransportSecurityState::Delegate* delegate) { | 57 TransportSecurityState::Delegate* delegate) { |
66 delegate_ = delegate; | 58 delegate_ = delegate; |
67 } | 59 } |
68 | 60 |
69 void TransportSecurityState::EnableHost(const std::string& host, | 61 void TransportSecurityState::EnableHost(const std::string& host, |
70 const DomainState& state) { | 62 const DomainState& state) { |
71 DCHECK(CalledOnValidThread()); | 63 DCHECK(CalledOnValidThread()); |
72 | 64 |
73 const std::string canonicalized_host = CanonicalizeHost(host); | 65 const std::string canonicalized_host = CanonicalizeHost(host); |
74 if (canonicalized_host.empty()) | 66 if (canonicalized_host.empty()) |
75 return; | 67 return; |
76 | 68 |
77 // Only override a preloaded state if the new state describes a more strict | |
78 // policy. TODO(palmer): Reconsider this? | |
79 DomainState existing_state; | 69 DomainState existing_state; |
80 if (IsPreloadedSTS(canonicalized_host, true, &existing_state) && | |
81 canonicalized_host == CanonicalizeHost(existing_state.domain) && | |
82 existing_state.IsMoreStrict(state)) { | |
83 return; | |
84 } | |
85 | 70 |
86 // Use the original creation date if we already have this host. | 71 // Use the original creation date if we already have this host. (But note |
72 // that statically-defined states have no |created| date. Therefore, we do | |
73 // not bother to search the SNI-only static states.) | |
87 DomainState state_copy(state); | 74 DomainState state_copy(state); |
88 if (GetDomainState(&existing_state, host, true) && | 75 if (GetDomainState(host, false /* sni_enabled */, &existing_state) && |
89 !existing_state.created.is_null()) { | 76 !existing_state.created.is_null()) { |
90 state_copy.created = existing_state.created; | 77 state_copy.created = existing_state.created; |
91 } | 78 } |
92 | 79 |
93 // We don't store these values. | 80 // No need to store this value since it is redundant. (|canonicalized_host| |
94 state_copy.preloaded = false; | 81 // is the map key.) |
95 state_copy.domain.clear(); | 82 state_copy.domain.clear(); |
96 | 83 |
97 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; | 84 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; |
98 DirtyNotify(); | 85 DirtyNotify(); |
99 } | 86 } |
100 | 87 |
101 bool TransportSecurityState::DeleteHost(const std::string& host) { | 88 bool TransportSecurityState::DeleteHost(const std::string& host) { |
102 DCHECK(CalledOnValidThread()); | 89 DCHECK(CalledOnValidThread()); |
103 | 90 |
104 const std::string canonicalized_host = CanonicalizeHost(host); | 91 const std::string canonicalized_host = CanonicalizeHost(host); |
105 if (canonicalized_host.empty()) | 92 if (canonicalized_host.empty()) |
106 return false; | 93 return false; |
107 | 94 |
108 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( | 95 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( |
109 HashHost(canonicalized_host)); | 96 HashHost(canonicalized_host)); |
110 if (i != enabled_hosts_.end()) { | 97 if (i != enabled_hosts_.end()) { |
111 enabled_hosts_.erase(i); | 98 enabled_hosts_.erase(i); |
112 DirtyNotify(); | 99 DirtyNotify(); |
113 return true; | 100 return true; |
114 } | 101 } |
115 return false; | 102 return false; |
116 } | 103 } |
117 | 104 |
118 bool TransportSecurityState::HasPinsForHost(DomainState* result, | 105 bool TransportSecurityState::GetDomainState(const std::string& host, |
119 const std::string& host, | 106 bool sni_enabled, |
120 bool sni_available) { | 107 DomainState* result) { |
121 DCHECK(CalledOnValidThread()); | |
122 | |
123 return HasMetadata(result, host, sni_available) && | |
124 (!result->dynamic_spki_hashes.empty() || | |
125 !result->preloaded_spki_hashes.empty()); | |
126 } | |
127 | |
128 bool TransportSecurityState::GetDomainState(DomainState* result, | |
129 const std::string& host, | |
130 bool sni_available) { | |
131 DCHECK(CalledOnValidThread()); | |
132 | |
133 return HasMetadata(result, host, sni_available); | |
134 } | |
135 | |
136 bool TransportSecurityState::HasMetadata(DomainState* result, | |
137 const std::string& host, | |
138 bool sni_available) { | |
139 DCHECK(CalledOnValidThread()); | 108 DCHECK(CalledOnValidThread()); |
140 | 109 |
141 DomainState state; | 110 DomainState state; |
142 const std::string canonicalized_host = CanonicalizeHost(host); | 111 const std::string canonicalized_host = CanonicalizeHost(host); |
143 if (canonicalized_host.empty()) | 112 if (canonicalized_host.empty()) |
144 return false; | 113 return false; |
145 | 114 |
146 bool has_preload = IsPreloadedSTS(canonicalized_host, sni_available, &state); | 115 bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, |
116 &state); | |
147 std::string canonicalized_preload = CanonicalizeHost(state.domain); | 117 std::string canonicalized_preload = CanonicalizeHost(state.domain); |
148 | 118 |
149 base::Time current_time(base::Time::Now()); | 119 base::Time current_time(base::Time::Now()); |
150 | 120 |
151 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 121 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
152 std::string host_sub_chunk(&canonicalized_host[i], | 122 std::string host_sub_chunk(&canonicalized_host[i], |
153 canonicalized_host.size() - i); | 123 canonicalized_host.size() - i); |
154 // Exact match of a preload always wins. | 124 // Exact match of a preload always wins. |
155 if (has_preload && host_sub_chunk == canonicalized_preload) { | 125 if (has_preload && host_sub_chunk == canonicalized_preload) { |
156 *result = state; | 126 *result = state; |
157 return true; | 127 return true; |
158 } | 128 } |
159 | 129 |
160 std::map<std::string, DomainState>::iterator j = | 130 std::map<std::string, DomainState>::iterator j = |
161 enabled_hosts_.find(HashHost(host_sub_chunk)); | 131 enabled_hosts_.find(HashHost(host_sub_chunk)); |
162 if (j == enabled_hosts_.end()) | 132 if (j == enabled_hosts_.end()) |
163 continue; | 133 continue; |
164 | 134 |
165 if (current_time > j->second.expiry && | 135 if (current_time > j->second.upgrade_expiry && |
166 current_time > j->second.dynamic_spki_hashes_expiry) { | 136 current_time > j->second.dynamic_spki_hashes_expiry) { |
167 enabled_hosts_.erase(j); | 137 enabled_hosts_.erase(j); |
168 DirtyNotify(); | 138 DirtyNotify(); |
169 continue; | 139 continue; |
170 } | 140 } |
171 | 141 |
172 state = j->second; | 142 state = j->second; |
173 state.domain = DNSDomainToString(host_sub_chunk); | 143 state.domain = DNSDomainToString(host_sub_chunk); |
174 | 144 |
175 // Succeed if we matched the domain exactly or if subdomain matches are | 145 // Succeed if we matched the domain exactly or if subdomain matches are |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
283 if (!base::Base64Decode(unquoted, &decoded) || | 253 if (!base::Base64Decode(unquoted, &decoded) || |
284 decoded.size() != arraysize(fp.data)) { | 254 decoded.size() != arraysize(fp.data)) { |
285 return false; | 255 return false; |
286 } | 256 } |
287 | 257 |
288 memcpy(fp.data, decoded.data(), arraysize(fp.data)); | 258 memcpy(fp.data, decoded.data(), arraysize(fp.data)); |
289 fingerprints->push_back(fp); | 259 fingerprints->push_back(fp); |
290 return true; | 260 return true; |
291 } | 261 } |
292 | 262 |
293 // static | |
294 bool TransportSecurityState::GetPublicKeyHash( | |
295 const X509Certificate& cert, SHA1Fingerprint* spki_hash) { | |
296 std::string der_bytes; | |
297 if (!X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der_bytes)) | |
298 return false; | |
299 | |
300 base::StringPiece spki; | |
301 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) | |
302 return false; | |
303 | |
304 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki.data()), | |
305 spki.size(), spki_hash->data); | |
306 | |
307 return true; | |
308 } | |
309 | |
310 struct FingerprintsEqualPredicate { | 263 struct FingerprintsEqualPredicate { |
311 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : | 264 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : |
312 fingerprint_(fingerprint) {} | 265 fingerprint_(fingerprint) {} |
313 | 266 |
314 bool operator()(const SHA1Fingerprint& other) const { | 267 bool operator()(const SHA1Fingerprint& other) const { |
315 return fingerprint_.Equals(other); | 268 return fingerprint_.Equals(other); |
316 } | 269 } |
317 | 270 |
318 const SHA1Fingerprint& fingerprint_; | 271 const SHA1Fingerprint& fingerprint_; |
319 }; | 272 }; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
361 if (from_cert_chain.empty()) | 314 if (from_cert_chain.empty()) |
362 return false; | 315 return false; |
363 | 316 |
364 return IsBackupPinPresent(pins, from_cert_chain) && | 317 return IsBackupPinPresent(pins, from_cert_chain) && |
365 HashesIntersect(pins, from_cert_chain); | 318 HashesIntersect(pins, from_cert_chain); |
366 } | 319 } |
367 | 320 |
368 // "Public-Key-Pins" ":" | 321 // "Public-Key-Pins" ":" |
369 // "max-age" "=" delta-seconds ";" | 322 // "max-age" "=" delta-seconds ";" |
370 // "pin-" algo "=" base64 [ ";" ... ] | 323 // "pin-" algo "=" base64 [ ";" ... ] |
371 // | 324 bool TransportSecurityState::DomainState::ParsePinsHeader( |
372 // static | 325 const base::Time& now, |
373 bool TransportSecurityState::ParsePinsHeader(const std::string& value, | 326 const std::string& value, |
374 const SSLInfo& ssl_info, | 327 const SSLInfo& ssl_info) { |
375 DomainState* state) { | |
376 bool parsed_max_age = false; | 328 bool parsed_max_age = false; |
377 int max_age = 0; | 329 int max_age_candidate = 0; |
378 FingerprintVector pins; | 330 FingerprintVector pins; |
379 | 331 |
380 std::string source = value; | 332 std::string source = value; |
381 | 333 |
382 while (!source.empty()) { | 334 while (!source.empty()) { |
383 StringPair semicolon = Split(source, ';'); | 335 StringPair semicolon = Split(source, ';'); |
384 semicolon.first = Strip(semicolon.first); | 336 semicolon.first = Strip(semicolon.first); |
385 semicolon.second = Strip(semicolon.second); | 337 semicolon.second = Strip(semicolon.second); |
386 StringPair equals = Split(semicolon.first, '='); | 338 StringPair equals = Split(semicolon.first, '='); |
387 equals.first = Strip(equals.first); | 339 equals.first = Strip(equals.first); |
388 equals.second = Strip(equals.second); | 340 equals.second = Strip(equals.second); |
389 | 341 |
390 if (LowerCaseEqualsASCII(equals.first, "max-age")) { | 342 if (LowerCaseEqualsASCII(equals.first, "max-age")) { |
391 if (equals.second.empty() || | 343 if (equals.second.empty() || |
392 !MaxAgeToInt(equals.second.begin(), equals.second.end(), &max_age)) { | 344 !MaxAgeToInt(equals.second.begin(), equals.second.end(), |
345 &max_age_candidate)) { | |
393 return false; | 346 return false; |
394 } | 347 } |
395 if (max_age > kMaxHSTSAgeSecs) | 348 if (max_age_candidate > kMaxHSTSAgeSecs) |
396 max_age = kMaxHSTSAgeSecs; | 349 max_age_candidate = kMaxHSTSAgeSecs; |
397 parsed_max_age = true; | 350 parsed_max_age = true; |
398 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { | 351 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { |
399 if (!ParseAndAppendPin(equals.second, &pins)) | 352 if (!ParseAndAppendPin(equals.second, &pins)) |
400 return false; | 353 return false; |
401 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | 354 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { |
402 // TODO(palmer) | 355 // TODO(palmer) |
403 } else { | 356 } else { |
404 // Silently ignore unknown directives for forward compatibility. | 357 // Silently ignore unknown directives for forward compatibility. |
405 } | 358 } |
406 | 359 |
407 source = semicolon.second; | 360 source = semicolon.second; |
408 } | 361 } |
409 | 362 |
410 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) | 363 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) |
411 return false; | 364 return false; |
412 | 365 |
413 state->max_age = max_age; | 366 dynamic_spki_hashes_expiry = |
414 state->dynamic_spki_hashes_expiry = | 367 now + base::TimeDelta::FromSeconds(max_age_candidate); |
415 base::Time::Now() + base::TimeDelta::FromSeconds(max_age); | |
416 | 368 |
417 state->dynamic_spki_hashes.clear(); | 369 dynamic_spki_hashes.clear(); |
418 if (max_age > 0) { | 370 if (max_age_candidate > 0) { |
419 for (FingerprintVector::const_iterator i = pins.begin(); | 371 for (FingerprintVector::const_iterator i = pins.begin(); |
420 i != pins.end(); i++) { | 372 i != pins.end(); ++i) { |
421 state->dynamic_spki_hashes.push_back(*i); | 373 dynamic_spki_hashes.push_back(*i); |
422 } | 374 } |
423 } | 375 } |
424 | 376 |
425 return true; | 377 return true; |
426 } | 378 } |
427 | 379 |
428 // "Strict-Transport-Security" ":" | 380 // "Strict-Transport-Security" ":" |
429 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] | 381 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] |
430 // | 382 bool TransportSecurityState::DomainState::ParseSTSHeader( |
431 // static | 383 const base::Time& now, |
432 bool TransportSecurityState::ParseHeader(const std::string& value, | 384 const std::string& value) { |
433 int* max_age, | |
434 bool* include_subdomains) { | |
435 DCHECK(max_age); | |
436 DCHECK(include_subdomains); | |
437 | |
438 int max_age_candidate = 0; | 385 int max_age_candidate = 0; |
439 | 386 |
440 enum ParserState { | 387 enum ParserState { |
441 START, | 388 START, |
442 AFTER_MAX_AGE_LABEL, | 389 AFTER_MAX_AGE_LABEL, |
443 AFTER_MAX_AGE_EQUALS, | 390 AFTER_MAX_AGE_EQUALS, |
444 AFTER_MAX_AGE, | 391 AFTER_MAX_AGE, |
445 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, | 392 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, |
446 AFTER_INCLUDE_SUBDOMAINS, | 393 AFTER_INCLUDE_SUBDOMAINS, |
447 } state = START; | 394 } state = START; |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
504 } | 451 } |
505 } | 452 } |
506 | 453 |
507 // We've consumed all the input. Let's see what state we ended up in. | 454 // We've consumed all the input. Let's see what state we ended up in. |
508 switch (state) { | 455 switch (state) { |
509 case START: | 456 case START: |
510 case AFTER_MAX_AGE_LABEL: | 457 case AFTER_MAX_AGE_LABEL: |
511 case AFTER_MAX_AGE_EQUALS: | 458 case AFTER_MAX_AGE_EQUALS: |
512 return false; | 459 return false; |
513 case AFTER_MAX_AGE: | 460 case AFTER_MAX_AGE: |
514 *max_age = max_age_candidate; | 461 upgrade_expiry = |
515 *include_subdomains = false; | 462 now + base::TimeDelta::FromSeconds(max_age_candidate); |
463 include_subdomains = false; | |
464 upgrade_mode = MODE_FORCE_HTTPS; | |
516 return true; | 465 return true; |
517 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: | 466 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: |
518 return false; | 467 return false; |
519 case AFTER_INCLUDE_SUBDOMAINS: | 468 case AFTER_INCLUDE_SUBDOMAINS: |
520 *max_age = max_age_candidate; | 469 upgrade_expiry = |
521 *include_subdomains = true; | 470 now + base::TimeDelta::FromSeconds(max_age_candidate); |
471 include_subdomains = true; | |
472 upgrade_mode = MODE_FORCE_HTTPS; | |
522 return true; | 473 return true; |
523 default: | 474 default: |
524 NOTREACHED(); | 475 NOTREACHED(); |
525 return false; | 476 return false; |
526 } | 477 } |
527 } | 478 } |
528 | 479 |
529 // Side pinning and superfluous certificates: | |
530 // | |
531 // In SSLClientSocketNSS::DoVerifyCertComplete we look for certificates with a | |
532 // Subject of CN=meta. When we find one we'll currently try and parse side | |
533 // pinned key from it. | |
534 // | |
535 // A side pin is a key which can be pinned to, but also can be kept offline and | |
536 // still held by the site owner. The CN=meta certificate is just a backwards | |
537 // compatiable method of carrying a lump of bytes to the client. (We could use | |
538 // a TLS extension just as well, but it's a lot easier for admins to add extra | |
539 // certificates to the chain.) | |
540 | |
541 // A TagMap represents the simple key-value structure that we use. Keys are | |
542 // 32-bit ints. Values are byte strings. | |
543 typedef std::map<uint32, base::StringPiece> TagMap; | |
544 | |
545 // ParseTags parses a list of key-value pairs from |in| to |out| and advances | |
546 // |in| past the data. The key-value pair data is: | |
547 // u16le num_tags | |
548 // u32le tag[num_tags] | |
549 // u16le lengths[num_tags] | |
550 // ...data... | |
551 static bool ParseTags(base::StringPiece* in, TagMap *out) { | |
552 // Many part of Chrome already assume little-endian. This is just to help | |
553 // anyone who should try to port it in the future. | |
554 #if defined(__BYTE_ORDER) | |
555 // Linux check | |
556 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian); | |
557 #elif defined(__BIG_ENDIAN__) | |
558 // Mac check | |
559 #error assumes little endian | |
560 #endif | |
561 | |
562 uint16 num_tags_16; | |
563 if (in->size() < sizeof(num_tags_16)) | |
564 return false; | |
565 | |
566 memcpy(&num_tags_16, in->data(), sizeof(num_tags_16)); | |
567 in->remove_prefix(sizeof(num_tags_16)); | |
568 unsigned num_tags = num_tags_16; | |
569 | |
570 if (in->size() < 6 * num_tags) | |
571 return false; | |
572 | |
573 const uint32* tags = reinterpret_cast<const uint32*>(in->data()); | |
574 const uint16* lens = reinterpret_cast<const uint16*>( | |
575 in->data() + 4*num_tags); | |
576 in->remove_prefix(6*num_tags); | |
577 | |
578 uint32 prev_tag = 0; | |
579 for (unsigned i = 0; i < num_tags; i++) { | |
580 size_t len = lens[i]; | |
581 uint32 tag = tags[i]; | |
582 | |
583 if (in->size() < len) | |
584 return false; | |
585 // tags must be in ascending order. | |
586 if (i > 0 && prev_tag >= tag) | |
587 return false; | |
588 (*out)[tag] = base::StringPiece(in->data(), len); | |
589 in->remove_prefix(len); | |
590 prev_tag = tag; | |
591 } | |
592 | |
593 return true; | |
594 } | |
595 | |
596 // GetTag extracts the data associated with |tag| in |tags|. | |
597 static bool GetTag(uint32 tag, const TagMap& tags, base::StringPiece* out) { | |
598 TagMap::const_iterator i = tags.find(tag); | |
599 if (i == tags.end()) | |
600 return false; | |
601 | |
602 *out = i->second; | |
603 return true; | |
604 } | |
605 | |
606 // kP256SubjectPublicKeyInfoPrefix can be prepended onto a P256 elliptic curve | |
607 // point in X9.62 format in order to make a valid SubjectPublicKeyInfo. The | |
608 // ASN.1 interpretation of these bytes is: | |
609 // | |
610 // 0:d=0 hl=2 l= 89 cons: SEQUENCE | |
611 // 2:d=1 hl=2 l= 19 cons: SEQUENCE | |
612 // 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey | |
613 // 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 | |
614 // 23:d=1 hl=2 l= 66 prim: BIT STRING | |
615 static const uint8 kP256SubjectPublicKeyInfoPrefix[] = { | |
616 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, | |
617 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, | |
618 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, | |
619 0x42, 0x00, | |
620 }; | |
621 | |
622 // VerifySignature returns true iff |sig| is a valid signature of | |
623 // |hash| by |pubkey|. The actual implementation is crypto library | |
624 // specific. | |
625 static bool VerifySignature(const base::StringPiece& pubkey, | |
626 const base::StringPiece& sig, | |
627 const base::StringPiece& hash); | |
628 | |
629 #if defined(USE_OPENSSL) | |
630 | |
631 static EVP_PKEY* DecodeX962P256PublicKey( | |
632 const base::StringPiece& pubkey_bytes) { | |
633 // The public key is an X9.62 encoded P256 point. | |
634 if (pubkey_bytes.size() != 1 + 2*32) | |
635 return NULL; | |
636 | |
637 std::string pubkey_spki( | |
638 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix), | |
639 sizeof(kP256SubjectPublicKeyInfoPrefix)); | |
640 pubkey_spki += pubkey_bytes.as_string(); | |
641 | |
642 EVP_PKEY* ret = NULL; | |
643 const unsigned char* der_pubkey = | |
644 reinterpret_cast<const unsigned char*>(pubkey_spki.data()); | |
645 d2i_PUBKEY(&ret, &der_pubkey, pubkey_spki.size()); | |
646 return ret; | |
647 } | |
648 | |
649 static bool VerifySignature(const base::StringPiece& pubkey, | |
650 const base::StringPiece& sig, | |
651 const base::StringPiece& hash) { | |
652 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> secpubkey( | |
653 DecodeX962P256PublicKey(pubkey)); | |
654 if (!secpubkey.get()) | |
655 return false; | |
656 | |
657 | |
658 crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ec_key( | |
659 EVP_PKEY_get1_EC_KEY(secpubkey.get())); | |
660 if (!ec_key.get()) | |
661 return false; | |
662 | |
663 return ECDSA_verify(0, reinterpret_cast<const unsigned char*>(hash.data()), | |
664 hash.size(), | |
665 reinterpret_cast<const unsigned char*>(sig.data()), | |
666 sig.size(), ec_key.get()) == 1; | |
667 } | |
668 | |
669 #else | |
670 | |
671 // DecodeX962P256PublicKey parses an uncompressed, X9.62 format, P256 elliptic | |
672 // curve point from |pubkey_bytes| and returns it as a SECKEYPublicKey. | |
673 static SECKEYPublicKey* DecodeX962P256PublicKey( | |
674 const base::StringPiece& pubkey_bytes) { | |
675 // The public key is an X9.62 encoded P256 point. | |
676 if (pubkey_bytes.size() != 1 + 2*32) | |
677 return NULL; | |
678 | |
679 std::string pubkey_spki( | |
680 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix), | |
681 sizeof(kP256SubjectPublicKeyInfoPrefix)); | |
682 pubkey_spki += pubkey_bytes.as_string(); | |
683 | |
684 SECItem der; | |
685 memset(&der, 0, sizeof(der)); | |
686 der.data = reinterpret_cast<uint8*>(const_cast<char*>(pubkey_spki.data())); | |
687 der.len = pubkey_spki.size(); | |
688 | |
689 CERTSubjectPublicKeyInfo* spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der); | |
690 if (!spki) | |
691 return NULL; | |
692 SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); | |
693 SECKEY_DestroySubjectPublicKeyInfo(spki); | |
694 | |
695 return public_key; | |
696 } | |
697 | |
698 static bool VerifySignature(const base::StringPiece& pubkey, | |
699 const base::StringPiece& sig, | |
700 const base::StringPiece& hash) { | |
701 SECKEYPublicKey* secpubkey = DecodeX962P256PublicKey(pubkey); | |
702 if (!secpubkey) | |
703 return false; | |
704 | |
705 SECItem sigitem; | |
706 memset(&sigitem, 0, sizeof(sigitem)); | |
707 sigitem.data = reinterpret_cast<uint8*>(const_cast<char*>(sig.data())); | |
708 sigitem.len = sig.size(); | |
709 | |
710 // |decoded_sigitem| is newly allocated, as is the data that it points to. | |
711 SECItem* decoded_sigitem = DSAU_DecodeDerSigToLen( | |
712 &sigitem, SECKEY_SignatureLen(secpubkey)); | |
713 | |
714 if (!decoded_sigitem) { | |
715 SECKEY_DestroyPublicKey(secpubkey); | |
716 return false; | |
717 } | |
718 | |
719 SECItem hashitem; | |
720 memset(&hashitem, 0, sizeof(hashitem)); | |
721 hashitem.data = reinterpret_cast<unsigned char*>( | |
722 const_cast<char*>(hash.data())); | |
723 hashitem.len = hash.size(); | |
724 | |
725 SECStatus rv = PK11_Verify(secpubkey, decoded_sigitem, &hashitem, NULL); | |
726 SECKEY_DestroyPublicKey(secpubkey); | |
727 SECITEM_FreeItem(decoded_sigitem, PR_TRUE); | |
728 return rv == SECSuccess; | |
729 } | |
730 | |
731 #endif // !defined(USE_OPENSSL) | |
732 | |
733 // These are the tag values that we use. Tags are little-endian on the wire and | |
734 // these values correspond to the ASCII of the name. | |
735 static const uint32 kTagALGO = 0x4f474c41; | |
736 static const uint32 kTagP256 = 0x36353250; | |
737 static const uint32 kTagPUBK = 0x4b425550; | |
738 static const uint32 kTagSIG = 0x474953; | |
739 static const uint32 kTagSPIN = 0x4e495053; | |
740 | |
741 // static | |
742 bool TransportSecurityState::ParseSidePin( | |
743 const base::StringPiece& leaf_spki, | |
744 const base::StringPiece& in_side_info, | |
745 FingerprintVector* out_pub_key_hash) { | |
746 base::StringPiece side_info(in_side_info); | |
747 | |
748 TagMap outer; | |
749 if (!ParseTags(&side_info, &outer)) | |
750 return false; | |
751 // trailing data is not allowed | |
752 if (side_info.size()) | |
753 return false; | |
754 | |
755 base::StringPiece side_pin_bytes; | |
756 if (!GetTag(kTagSPIN, outer, &side_pin_bytes)) | |
757 return false; | |
758 | |
759 bool have_parsed_a_key = false; | |
760 uint8 leaf_spki_hash[crypto::kSHA256Length]; | |
761 bool have_leaf_spki_hash = false; | |
762 | |
763 while (side_pin_bytes.size() > 0) { | |
764 TagMap side_pin; | |
765 if (!ParseTags(&side_pin_bytes, &side_pin)) | |
766 return false; | |
767 | |
768 base::StringPiece algo, pubkey, sig; | |
769 if (!GetTag(kTagALGO, side_pin, &algo) || | |
770 !GetTag(kTagPUBK, side_pin, &pubkey) || | |
771 !GetTag(kTagSIG, side_pin, &sig)) { | |
772 return false; | |
773 } | |
774 | |
775 if (algo.size() != sizeof(kTagP256) || | |
776 0 != memcmp(algo.data(), &kTagP256, sizeof(kTagP256))) { | |
777 // We don't support anything but P256 at the moment. | |
778 continue; | |
779 } | |
780 | |
781 if (!have_leaf_spki_hash) { | |
782 crypto::SHA256HashString( | |
783 leaf_spki.as_string(), leaf_spki_hash, sizeof(leaf_spki_hash)); | |
784 have_leaf_spki_hash = true; | |
785 } | |
786 | |
787 if (VerifySignature(pubkey, sig, base::StringPiece( | |
788 reinterpret_cast<const char*>(leaf_spki_hash), | |
789 sizeof(leaf_spki_hash)))) { | |
790 SHA1Fingerprint fpr; | |
791 base::SHA1HashBytes( | |
792 reinterpret_cast<const uint8*>(pubkey.data()), | |
793 pubkey.size(), | |
794 fpr.data); | |
795 out_pub_key_hash->push_back(fpr); | |
796 have_parsed_a_key = true; | |
797 } | |
798 } | |
799 | |
800 return have_parsed_a_key; | |
801 } | |
802 | |
803 // This function converts the binary hashes, which we store in | |
804 // |enabled_hosts_|, to a base64 string which we can include in a JSON file. | |
805 static std::string HashedDomainToExternalString(const std::string& hashed) { | |
806 std::string out; | |
807 CHECK(base::Base64Encode(hashed, &out)); | |
808 return out; | |
809 } | |
810 | |
811 // This inverts |HashedDomainToExternalString|, above. It turns an external | |
812 // string (from a JSON file) into an internal (binary) string. | |
813 static std::string ExternalStringToHashedDomain(const std::string& external) { | |
814 std::string out; | |
815 if (!base::Base64Decode(external, &out) || | |
816 out.size() != crypto::kSHA256Length) { | |
817 return std::string(); | |
818 } | |
819 | |
820 return out; | |
821 } | |
822 | |
823 static ListValue* SPKIHashesToListValue(const FingerprintVector& hashes) { | |
824 ListValue* pins = new ListValue; | |
825 | |
826 for (FingerprintVector::const_iterator i = hashes.begin(); | |
827 i != hashes.end(); ++i) { | |
828 std::string hash_str(reinterpret_cast<const char*>(i->data), | |
829 sizeof(i->data)); | |
830 std::string b64; | |
831 base::Base64Encode(hash_str, &b64); | |
832 pins->Append(new StringValue("sha1/" + b64)); | |
833 } | |
834 | |
835 return pins; | |
836 } | |
837 | |
838 bool TransportSecurityState::Serialise(std::string* output) { | |
839 DCHECK(CalledOnValidThread()); | |
840 | |
841 DictionaryValue toplevel; | |
842 base::Time now = base::Time::Now(); | |
843 for (std::map<std::string, DomainState>::const_iterator | |
844 i = enabled_hosts_.begin(); i != enabled_hosts_.end(); ++i) { | |
845 DictionaryValue* state = new DictionaryValue; | |
846 state->SetBoolean("include_subdomains", i->second.include_subdomains); | |
847 state->SetDouble("created", i->second.created.ToDoubleT()); | |
848 state->SetDouble("expiry", i->second.expiry.ToDoubleT()); | |
849 state->SetDouble("dynamic_spki_hashes_expiry", | |
850 i->second.dynamic_spki_hashes_expiry.ToDoubleT()); | |
851 | |
852 switch (i->second.mode) { | |
853 case DomainState::MODE_STRICT: | |
854 state->SetString("mode", "strict"); | |
855 break; | |
856 case DomainState::MODE_SPDY_ONLY: | |
857 state->SetString("mode", "spdy-only"); | |
858 break; | |
859 case DomainState::MODE_PINNING_ONLY: | |
860 state->SetString("mode", "pinning-only"); | |
861 break; | |
862 default: | |
863 NOTREACHED() << "DomainState with unknown mode"; | |
864 delete state; | |
865 continue; | |
866 } | |
867 | |
868 state->Set("preloaded_spki_hashes", | |
869 SPKIHashesToListValue(i->second.preloaded_spki_hashes)); | |
870 | |
871 if (now < i->second.dynamic_spki_hashes_expiry) { | |
872 state->Set("dynamic_spki_hashes", | |
873 SPKIHashesToListValue(i->second.dynamic_spki_hashes)); | |
874 } | |
875 | |
876 toplevel.Set(HashedDomainToExternalString(i->first), state); | |
877 } | |
878 | |
879 base::JSONWriter::WriteWithOptions(&toplevel, | |
880 base::JSONWriter::OPTIONS_PRETTY_PRINT, | |
881 output); | |
882 return true; | |
883 } | |
884 | |
885 bool TransportSecurityState::LoadEntries(const std::string& input, | |
886 bool* dirty) { | |
887 DCHECK(CalledOnValidThread()); | |
888 | |
889 enabled_hosts_.clear(); | |
890 return Deserialise(input, dirty, &enabled_hosts_); | |
891 } | |
892 | |
893 static bool AddHash(const std::string& type_and_base64, | 480 static bool AddHash(const std::string& type_and_base64, |
894 FingerprintVector* out) { | 481 FingerprintVector* out) { |
895 SHA1Fingerprint hash; | 482 SHA1Fingerprint hash; |
896 | 483 |
897 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | 484 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) |
898 return false; | 485 return false; |
899 | 486 |
900 out->push_back(hash); | 487 out->push_back(hash); |
901 return true; | 488 return true; |
902 } | 489 } |
903 | 490 |
904 static void SPKIHashesFromListValue(FingerprintVector* hashes, | |
905 const ListValue& pins) { | |
906 size_t num_pins = pins.GetSize(); | |
907 for (size_t i = 0; i < num_pins; ++i) { | |
908 std::string type_and_base64; | |
909 if (pins.GetString(i, &type_and_base64)) | |
910 AddHash(type_and_base64, hashes); | |
911 } | |
912 } | |
913 | |
914 // static | |
915 bool TransportSecurityState::Deserialise( | |
916 const std::string& input, | |
917 bool* dirty, | |
918 std::map<std::string, DomainState>* out) { | |
919 scoped_ptr<Value> value(base::JSONReader::Read(input)); | |
920 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) | |
921 return false; | |
922 | |
923 DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get()); | |
924 const base::Time current_time(base::Time::Now()); | |
925 bool dirtied = false; | |
926 | |
927 for (DictionaryValue::key_iterator i = dict_value->begin_keys(); | |
928 i != dict_value->end_keys(); ++i) { | |
929 DictionaryValue* state; | |
930 if (!dict_value->GetDictionaryWithoutPathExpansion(*i, &state)) | |
931 continue; | |
932 | |
933 bool include_subdomains; | |
934 std::string mode_string; | |
935 double created; | |
936 double expiry; | |
937 double dynamic_spki_hashes_expiry = 0.0; | |
938 | |
939 if (!state->GetBoolean("include_subdomains", &include_subdomains) || | |
940 !state->GetString("mode", &mode_string) || | |
941 !state->GetDouble("expiry", &expiry)) { | |
942 continue; | |
943 } | |
944 | |
945 // Don't fail if this key is not present. | |
946 (void) state->GetDouble("dynamic_spki_hashes_expiry", | |
947 &dynamic_spki_hashes_expiry); | |
948 | |
949 ListValue* pins_list = NULL; | |
950 FingerprintVector preloaded_spki_hashes; | |
951 if (state->GetList("preloaded_spki_hashes", &pins_list)) | |
952 SPKIHashesFromListValue(&preloaded_spki_hashes, *pins_list); | |
953 | |
954 FingerprintVector dynamic_spki_hashes; | |
955 if (state->GetList("dynamic_spki_hashes", &pins_list)) | |
956 SPKIHashesFromListValue(&dynamic_spki_hashes, *pins_list); | |
957 | |
958 DomainState::Mode mode; | |
959 if (mode_string == "strict") { | |
960 mode = DomainState::MODE_STRICT; | |
961 } else if (mode_string == "spdy-only") { | |
962 mode = DomainState::MODE_SPDY_ONLY; | |
963 } else if (mode_string == "pinning-only") { | |
964 mode = DomainState::MODE_PINNING_ONLY; | |
965 } else { | |
966 LOG(WARNING) << "Unknown TransportSecurityState mode string found: " | |
967 << mode_string; | |
968 continue; | |
969 } | |
970 | |
971 base::Time expiry_time = base::Time::FromDoubleT(expiry); | |
972 base::Time dynamic_spki_hashes_expiry_time = | |
973 base::Time::FromDoubleT(dynamic_spki_hashes_expiry); | |
974 base::Time created_time; | |
975 if (state->GetDouble("created", &created)) { | |
976 created_time = base::Time::FromDoubleT(created); | |
977 } else { | |
978 // We're migrating an old entry with no creation date. Make sure we | |
979 // write the new date back in a reasonable time frame. | |
980 dirtied = true; | |
981 created_time = base::Time::Now(); | |
982 } | |
983 | |
984 if (expiry_time <= current_time && | |
985 dynamic_spki_hashes_expiry_time <= current_time) { | |
986 // Make sure we dirty the state if we drop an entry. | |
987 dirtied = true; | |
988 continue; | |
989 } | |
990 | |
991 std::string hashed = ExternalStringToHashedDomain(*i); | |
992 if (hashed.empty()) { | |
993 dirtied = true; | |
994 continue; | |
995 } | |
996 | |
997 DomainState new_state; | |
998 new_state.mode = mode; | |
999 new_state.created = created_time; | |
1000 new_state.expiry = expiry_time; | |
1001 new_state.include_subdomains = include_subdomains; | |
1002 new_state.preloaded_spki_hashes = preloaded_spki_hashes; | |
1003 new_state.dynamic_spki_hashes = dynamic_spki_hashes; | |
1004 new_state.dynamic_spki_hashes_expiry = dynamic_spki_hashes_expiry_time; | |
1005 (*out)[hashed] = new_state; | |
1006 } | |
1007 | |
1008 *dirty = dirtied; | |
1009 return true; | |
1010 } | |
1011 | |
1012 TransportSecurityState::~TransportSecurityState() { | 491 TransportSecurityState::~TransportSecurityState() { |
1013 } | 492 } |
Ryan Sleevi
2012/04/26 19:21:12
nit: move } to line 491
palmer
2012/04/27 23:52:34
Done.
| |
1014 | 493 |
1015 void TransportSecurityState::DirtyNotify() { | 494 void TransportSecurityState::DirtyNotify() { |
1016 DCHECK(CalledOnValidThread()); | 495 DCHECK(CalledOnValidThread()); |
1017 | 496 |
1018 if (delegate_) | 497 if (delegate_) |
1019 delegate_->StateIsDirty(this); | 498 delegate_->StateIsDirty(this); |
1020 } | 499 } |
1021 | 500 |
1022 // static | 501 // static |
1023 std::string TransportSecurityState::CanonicalizeHost(const std::string& host) { | 502 std::string TransportSecurityState::CanonicalizeHost(const std::string& host) { |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1115 for (size_t j = 0; j < num_entries; j++) { | 594 for (size_t j = 0; j < num_entries; j++) { |
1116 if (entries[j].length == canonicalized_host.size() - i && | 595 if (entries[j].length == canonicalized_host.size() - i && |
1117 memcmp(entries[j].dns_name, &canonicalized_host[i], | 596 memcmp(entries[j].dns_name, &canonicalized_host[i], |
1118 entries[j].length) == 0) { | 597 entries[j].length) == 0) { |
1119 if (!entries[j].include_subdomains && i != 0) { | 598 if (!entries[j].include_subdomains && i != 0) { |
1120 *ret = false; | 599 *ret = false; |
1121 } else { | 600 } else { |
1122 out->include_subdomains = entries[j].include_subdomains; | 601 out->include_subdomains = entries[j].include_subdomains; |
1123 *ret = true; | 602 *ret = true; |
1124 if (!entries[j].https_required) | 603 if (!entries[j].https_required) |
1125 out->mode = TransportSecurityState::DomainState::MODE_PINNING_ONLY; | 604 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; |
1126 if (entries[j].pins.required_hashes) { | 605 if (entries[j].pins.required_hashes) { |
1127 const char* const* hash = entries[j].pins.required_hashes; | 606 const char* const* hash = entries[j].pins.required_hashes; |
1128 while (*hash) { | 607 while (*hash) { |
1129 bool ok = AddHash(*hash, &out->preloaded_spki_hashes); | 608 bool ok = AddHash(*hash, &out->static_spki_hashes); |
1130 DCHECK(ok) << " failed to parse " << *hash; | 609 DCHECK(ok) << " failed to parse " << *hash; |
1131 hash++; | 610 hash++; |
1132 } | 611 } |
1133 } | 612 } |
1134 if (entries[j].pins.excluded_hashes) { | 613 if (entries[j].pins.excluded_hashes) { |
1135 const char* const* hash = entries[j].pins.excluded_hashes; | 614 const char* const* hash = entries[j].pins.excluded_hashes; |
1136 while (*hash) { | 615 while (*hash) { |
1137 bool ok = AddHash(*hash, &out->bad_preloaded_spki_hashes); | 616 bool ok = AddHash(*hash, &out->bad_static_spki_hashes); |
1138 DCHECK(ok) << " failed to parse " << *hash; | 617 DCHECK(ok) << " failed to parse " << *hash; |
1139 hash++; | 618 hash++; |
1140 } | 619 } |
1141 } | 620 } |
1142 } | 621 } |
1143 return true; | 622 return true; |
1144 } | 623 } |
1145 } | 624 } |
1146 return false; | 625 return false; |
1147 } | 626 } |
(...skipping 22 matching lines...) Expand all Loading... | |
1170 return entry; | 649 return entry; |
1171 } | 650 } |
1172 } | 651 } |
1173 } | 652 } |
1174 | 653 |
1175 return NULL; | 654 return NULL; |
1176 } | 655 } |
1177 | 656 |
1178 // static | 657 // static |
1179 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | 658 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, |
1180 bool sni_available) { | 659 bool sni_enabled) { |
1181 std::string canonicalized_host = CanonicalizeHost(host); | 660 std::string canonicalized_host = CanonicalizeHost(host); |
1182 const struct HSTSPreload* entry = | 661 const struct HSTSPreload* entry = |
1183 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 662 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
1184 | 663 |
1185 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 664 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
1186 return true; | 665 return true; |
1187 | 666 |
1188 if (sni_available) { | 667 if (sni_enabled) { |
1189 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 668 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, |
1190 kNumPreloadedSNISTS); | 669 kNumPreloadedSNISTS); |
1191 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 670 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
1192 return true; | 671 return true; |
1193 } | 672 } |
1194 | 673 |
1195 return false; | 674 return false; |
1196 } | 675 } |
1197 | 676 |
1198 // static | 677 // static |
1199 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | 678 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { |
1200 std::string canonicalized_host = CanonicalizeHost(host); | 679 std::string canonicalized_host = CanonicalizeHost(host); |
1201 | 680 |
1202 const struct HSTSPreload* entry = | 681 const struct HSTSPreload* entry = |
1203 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 682 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
1204 | 683 |
1205 if (!entry) { | 684 if (!entry) { |
1206 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 685 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, |
1207 kNumPreloadedSNISTS); | 686 kNumPreloadedSNISTS); |
1208 } | 687 } |
1209 | 688 |
1210 DCHECK(entry); | 689 DCHECK(entry); |
1211 DCHECK(entry->pins.required_hashes); | 690 DCHECK(entry->pins.required_hashes); |
1212 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | 691 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); |
1213 | 692 |
1214 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | 693 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
1215 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | 694 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); |
1216 } | 695 } |
1217 | 696 |
1218 // IsPreloadedSTS returns true if the canonicalized hostname should always be | 697 bool TransportSecurityState::GetStaticDomainState( |
1219 // considered to have STS enabled. | |
1220 bool TransportSecurityState::IsPreloadedSTS( | |
1221 const std::string& canonicalized_host, | 698 const std::string& canonicalized_host, |
1222 bool sni_available, | 699 bool sni_enabled, |
1223 DomainState* out) { | 700 DomainState* out) { |
1224 DCHECK(CalledOnValidThread()); | 701 DCHECK(CalledOnValidThread()); |
1225 | 702 |
1226 out->preloaded = true; | 703 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
1227 out->mode = DomainState::MODE_STRICT; | |
1228 out->include_subdomains = false; | 704 out->include_subdomains = false; |
1229 | 705 |
1230 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 706 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
1231 std::string host_sub_chunk(&canonicalized_host[i], | 707 std::string host_sub_chunk(&canonicalized_host[i], |
1232 canonicalized_host.size() - i); | 708 canonicalized_host.size() - i); |
1233 out->domain = DNSDomainToString(host_sub_chunk); | 709 out->domain = DNSDomainToString(host_sub_chunk); |
1234 std::string hashed_host(HashHost(host_sub_chunk)); | 710 std::string hashed_host(HashHost(host_sub_chunk)); |
1235 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { | 711 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { |
1236 *out = forced_hosts_[hashed_host]; | 712 *out = forced_hosts_[hashed_host]; |
1237 out->domain = DNSDomainToString(host_sub_chunk); | 713 out->domain = DNSDomainToString(host_sub_chunk); |
1238 out->preloaded = true; | |
1239 return true; | 714 return true; |
1240 } | 715 } |
1241 bool ret; | 716 bool ret; |
1242 if (HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, | 717 if (HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, |
1243 &ret)) { | 718 &ret)) { |
1244 return ret; | 719 return ret; |
1245 } | 720 } |
1246 if (sni_available && | 721 if (sni_enabled && |
1247 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, | 722 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, |
1248 out, &ret)) { | 723 out, &ret)) { |
1249 return ret; | 724 return ret; |
1250 } | 725 } |
1251 } | 726 } |
1252 | 727 |
1253 return false; | 728 return false; |
1254 } | 729 } |
1255 | 730 |
1256 static std::string HashesToBase64String( | 731 static std::string HashesToBase64String( |
1257 const FingerprintVector& hashes) { | 732 const FingerprintVector& hashes) { |
1258 std::vector<std::string> hashes_strs; | 733 std::vector<std::string> hashes_strs; |
1259 for (FingerprintVector::const_iterator | 734 for (FingerprintVector::const_iterator |
1260 i = hashes.begin(); i != hashes.end(); i++) { | 735 i = hashes.begin(); i != hashes.end(); i++) { |
1261 std::string s; | 736 std::string s; |
1262 const std::string hash_str(reinterpret_cast<const char*>(i->data), | 737 const std::string hash_str(reinterpret_cast<const char*>(i->data), |
1263 sizeof(i->data)); | 738 sizeof(i->data)); |
1264 base::Base64Encode(hash_str, &s); | 739 base::Base64Encode(hash_str, &s); |
1265 hashes_strs.push_back(s); | 740 hashes_strs.push_back(s); |
1266 } | 741 } |
1267 | 742 |
1268 return JoinString(hashes_strs, ','); | 743 return JoinString(hashes_strs, ','); |
1269 } | 744 } |
1270 | 745 |
1271 TransportSecurityState::DomainState::DomainState() | 746 TransportSecurityState::DomainState::DomainState() |
1272 : mode(MODE_STRICT), | 747 : upgrade_mode(MODE_FORCE_HTTPS), |
1273 created(base::Time::Now()), | 748 created(base::Time::Now()), |
1274 include_subdomains(false), | 749 include_subdomains(false) { |
1275 preloaded(false) { | |
1276 } | 750 } |
1277 | 751 |
1278 TransportSecurityState::DomainState::~DomainState() { | 752 TransportSecurityState::DomainState::~DomainState() { |
1279 } | 753 } |
1280 | 754 |
1281 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( | 755 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
1282 const FingerprintVector& hashes) { | 756 const FingerprintVector& hashes) const { |
1283 | 757 if (HashesIntersect(bad_static_spki_hashes, hashes)) { |
1284 if (HashesIntersect(bad_preloaded_spki_hashes, hashes)) { | |
1285 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 758 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
1286 << ". Validated chain: " << HashesToBase64String(hashes) | 759 << ". Validated chain: " << HashesToBase64String(hashes) |
1287 << ", matches one or more bad hashes: " | 760 << ", matches one or more bad hashes: " |
1288 << HashesToBase64String(bad_preloaded_spki_hashes); | 761 << HashesToBase64String(bad_static_spki_hashes); |
1289 return false; | 762 return false; |
1290 } | 763 } |
1291 | 764 |
1292 if (!(dynamic_spki_hashes.empty() && preloaded_spki_hashes.empty()) && | 765 if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) && |
1293 !HashesIntersect(dynamic_spki_hashes, hashes) && | 766 !HashesIntersect(dynamic_spki_hashes, hashes) && |
1294 !HashesIntersect(preloaded_spki_hashes, hashes)) { | 767 !HashesIntersect(static_spki_hashes, hashes)) { |
1295 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 768 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
1296 << ". Validated chain: " << HashesToBase64String(hashes) | 769 << ". Validated chain: " << HashesToBase64String(hashes) |
1297 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) | 770 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) |
1298 << " or: " << HashesToBase64String(preloaded_spki_hashes); | 771 << " or: " << HashesToBase64String(static_spki_hashes); |
1299 | 772 |
1300 return false; | 773 return false; |
1301 } | 774 } |
1302 | 775 |
1303 return true; | 776 return true; |
1304 } | 777 } |
1305 | 778 |
1306 bool TransportSecurityState::DomainState::IsMoreStrict( | 779 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { |
1307 const TransportSecurityState::DomainState& other) { | 780 return upgrade_mode == MODE_FORCE_HTTPS; |
1308 if (this->dynamic_spki_hashes.empty() && !other.dynamic_spki_hashes.empty()) | |
1309 return false; | |
1310 | |
1311 if (!this->include_subdomains && other.include_subdomains) | |
1312 return false; | |
1313 | |
1314 return true; | |
1315 } | 781 } |
1316 | 782 |
1317 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() | 783 bool TransportSecurityState::DomainState::HasPins() const { |
1318 const { | 784 return static_spki_hashes.size() > 0 || |
1319 return mode == MODE_STRICT; | 785 bad_static_spki_hashes.size() > 0 || |
786 dynamic_spki_hashes.size() > 0; | |
1320 } | 787 } |
1321 | 788 |
1322 } // namespace | 789 } // namespace |
OLD | NEW |