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/public_key_hashes.h" | 35 #include "net/base/public_key_hashes.h" |
39 #include "net/base/ssl_info.h" | 36 #include "net/base/ssl_info.h" |
40 #include "net/base/x509_certificate.h" | 37 #include "net/base/x509_certificate.h" |
41 #include "net/http/http_util.h" | 38 #include "net/http/http_util.h" |
42 | 39 |
43 #if defined(USE_OPENSSL) | 40 #if defined(USE_OPENSSL) |
44 #include "crypto/openssl_util.h" | 41 #include "crypto/openssl_util.h" |
45 #endif | 42 #endif |
46 | 43 |
47 namespace net { | 44 namespace net { |
48 | 45 |
49 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year | 46 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year |
50 | 47 |
48 /* XXX | |
Ryan Sleevi
2012/03/28 00:50:32
Delete?
palmer
2012/04/10 23:25:51
Done.
| |
51 TransportSecurityState::TransportSecurityState(const std::string& hsts_hosts) | 49 TransportSecurityState::TransportSecurityState(const std::string& hsts_hosts) |
52 : delegate_(NULL) { | 50 : delegate_(NULL) { |
53 if (!hsts_hosts.empty()) { | 51 if (!hsts_hosts.empty()) { |
54 bool dirty; | 52 bool dirty; |
55 Deserialise(hsts_hosts, &dirty, &forced_hosts_); | 53 Deserialise(hsts_hosts, &dirty, &forced_hosts_); |
56 } | 54 } |
57 } | 55 }*/ |
58 | 56 |
59 static std::string HashHost(const std::string& canonicalized_host) { | 57 static std::string HashHost(const std::string& canonicalized_host) { |
60 char hashed[crypto::kSHA256Length]; | 58 char hashed[crypto::kSHA256Length]; |
61 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | 59 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); |
62 return std::string(hashed, sizeof(hashed)); | 60 return std::string(hashed, sizeof(hashed)); |
63 } | 61 } |
64 | 62 |
63 TransportSecurityState::TransportSecurityState( | |
64 TransportSecurityState::Delegate* delegate) | |
65 : delegate_(delegate) { } | |
66 | |
67 TransportSecurityState::TransportSecurityState() | |
68 : delegate_(NULL) { } | |
69 | |
65 void TransportSecurityState::SetDelegate( | 70 void TransportSecurityState::SetDelegate( |
66 TransportSecurityState::Delegate* delegate) { | 71 TransportSecurityState::Delegate* delegate) { |
67 delegate_ = delegate; | 72 delegate_ = delegate; |
68 } | 73 } |
69 | 74 |
70 void TransportSecurityState::EnableHost(const std::string& host, | 75 void TransportSecurityState::EnableHost(const std::string& host, |
71 const DomainState& state) { | 76 const DomainState& state) { |
72 DCHECK(CalledOnValidThread()); | 77 DCHECK(CalledOnValidThread()); |
73 | 78 |
74 const std::string canonicalized_host = CanonicalizeHost(host); | 79 const std::string canonicalized_host = CanonicalizeHost(host); |
75 if (canonicalized_host.empty()) | 80 if (canonicalized_host.empty()) |
76 return; | 81 return; |
77 | 82 |
83 DomainState existing_state; | |
84 | |
78 // Only override a preloaded state if the new state describes a more strict | 85 // Only override a preloaded state if the new state describes a more strict |
79 // policy. TODO(palmer): Reconsider this? | 86 // policy. TODO(palmer): Reconsider this? |
80 DomainState existing_state; | 87 /*if (GetStaticDomainState(canonicalized_host, true, &existing_state) && |
81 if (IsPreloadedSTS(canonicalized_host, true, &existing_state) && | |
82 canonicalized_host == CanonicalizeHost(existing_state.domain) && | 88 canonicalized_host == CanonicalizeHost(existing_state.domain) && |
83 existing_state.IsMoreStrict(state)) { | 89 existing_state.IsMoreStrict(state)) { |
84 return; | 90 return; |
85 } | 91 }*/ |
Ryan Sleevi
2012/03/28 00:50:32
Delete?
palmer
2012/04/10 23:25:51
Done.
| |
86 | 92 |
87 // Use the original creation date if we already have this host. | 93 // Use the original creation date if we already have this host. |
88 DomainState state_copy(state); | 94 DomainState state_copy(state); |
89 if (GetDomainState(&existing_state, host, true) && | 95 if (GetDomainState(host, true, &existing_state) && |
Ryan Sleevi
2012/03/28 00:50:32
nit: GetDomainState(host, true /*sni_enabled*/, &e
palmer
2012/04/10 23:25:51
It doesn't really matter, since statically-defined
| |
90 !existing_state.created.is_null()) { | 96 !existing_state.created.is_null()) { |
91 state_copy.created = existing_state.created; | 97 state_copy.created = existing_state.created; |
92 } | 98 } |
93 | 99 |
94 // We don't store these values. | 100 // We don't store this value. |
Ryan Sleevi
2012/03/28 00:50:32
nit: Drop the "we", expand on why this value isn't
palmer
2012/04/10 23:25:51
Done.
| |
95 state_copy.preloaded = false; | |
96 state_copy.domain.clear(); | 101 state_copy.domain.clear(); |
97 | 102 |
98 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; | 103 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; |
99 DirtyNotify(); | 104 DirtyNotify(); |
100 } | 105 } |
101 | 106 |
102 bool TransportSecurityState::DeleteHost(const std::string& host) { | 107 bool TransportSecurityState::DeleteHost(const std::string& host) { |
103 DCHECK(CalledOnValidThread()); | 108 DCHECK(CalledOnValidThread()); |
104 | 109 |
105 const std::string canonicalized_host = CanonicalizeHost(host); | 110 const std::string canonicalized_host = CanonicalizeHost(host); |
106 if (canonicalized_host.empty()) | 111 if (canonicalized_host.empty()) |
107 return false; | 112 return false; |
108 | 113 |
109 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( | 114 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( |
110 HashHost(canonicalized_host)); | 115 HashHost(canonicalized_host)); |
111 if (i != enabled_hosts_.end()) { | 116 if (i != enabled_hosts_.end()) { |
112 enabled_hosts_.erase(i); | 117 enabled_hosts_.erase(i); |
113 DirtyNotify(); | 118 DirtyNotify(); |
114 return true; | 119 return true; |
115 } | 120 } |
116 return false; | 121 return false; |
117 } | 122 } |
118 | 123 |
119 bool TransportSecurityState::HasPinsForHost(DomainState* result, | 124 bool TransportSecurityState::GetDomainState(const std::string& host, |
120 const std::string& host, | 125 bool sni_enabled, |
121 bool sni_available) { | 126 DomainState* result) { |
122 DCHECK(CalledOnValidThread()); | |
123 | |
124 return HasMetadata(result, host, sni_available) && | |
125 (!result->dynamic_spki_hashes.empty() || | |
126 !result->preloaded_spki_hashes.empty()); | |
127 } | |
128 | |
129 bool TransportSecurityState::GetDomainState(DomainState* result, | |
130 const std::string& host, | |
131 bool sni_available) { | |
132 DCHECK(CalledOnValidThread()); | |
133 | |
134 return HasMetadata(result, host, sni_available); | |
135 } | |
136 | |
137 bool TransportSecurityState::HasMetadata(DomainState* result, | |
138 const std::string& host, | |
139 bool sni_available) { | |
140 DCHECK(CalledOnValidThread()); | 127 DCHECK(CalledOnValidThread()); |
141 | 128 |
142 DomainState state; | 129 DomainState state; |
143 const std::string canonicalized_host = CanonicalizeHost(host); | 130 const std::string canonicalized_host = CanonicalizeHost(host); |
144 if (canonicalized_host.empty()) | 131 if (canonicalized_host.empty()) |
145 return false; | 132 return false; |
146 | 133 |
147 bool has_preload = IsPreloadedSTS(canonicalized_host, sni_available, &state); | 134 bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, |
135 &state); | |
148 std::string canonicalized_preload = CanonicalizeHost(state.domain); | 136 std::string canonicalized_preload = CanonicalizeHost(state.domain); |
149 | 137 |
150 base::Time current_time(base::Time::Now()); | 138 base::Time current_time(base::Time::Now()); |
151 | 139 |
152 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 140 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
153 std::string host_sub_chunk(&canonicalized_host[i], | 141 std::string host_sub_chunk(&canonicalized_host[i], |
154 canonicalized_host.size() - i); | 142 canonicalized_host.size() - i); |
155 // Exact match of a preload always wins. | 143 // Exact match of a preload always wins. |
156 if (has_preload && host_sub_chunk == canonicalized_preload) { | 144 if (has_preload && host_sub_chunk == canonicalized_preload) { |
157 *result = state; | 145 *result = state; |
158 return true; | 146 return true; |
159 } | 147 } |
160 | 148 |
161 std::map<std::string, DomainState>::iterator j = | 149 std::map<std::string, DomainState>::iterator j = |
162 enabled_hosts_.find(HashHost(host_sub_chunk)); | 150 enabled_hosts_.find(HashHost(host_sub_chunk)); |
163 if (j == enabled_hosts_.end()) | 151 if (j == enabled_hosts_.end()) |
164 continue; | 152 continue; |
165 | 153 |
166 if (current_time > j->second.expiry && | 154 if (current_time > j->second.upgrade_expiry && |
167 current_time > j->second.dynamic_spki_hashes_expiry) { | 155 current_time > j->second.dynamic_spki_hashes_expiry) { |
168 enabled_hosts_.erase(j); | 156 enabled_hosts_.erase(j); |
169 DirtyNotify(); | 157 DirtyNotify(); |
170 continue; | 158 continue; |
171 } | 159 } |
172 | 160 |
173 state = j->second; | 161 state = j->second; |
174 state.domain = DNSDomainToString(host_sub_chunk); | 162 state.domain = DNSDomainToString(host_sub_chunk); |
175 | 163 |
176 // Succeed if we matched the domain exactly or if subdomain matches are | 164 // 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... | |
284 if (!base::Base64Decode(unquoted, &decoded) || | 272 if (!base::Base64Decode(unquoted, &decoded) || |
285 decoded.size() != arraysize(fp.data)) { | 273 decoded.size() != arraysize(fp.data)) { |
286 return false; | 274 return false; |
287 } | 275 } |
288 | 276 |
289 memcpy(fp.data, decoded.data(), arraysize(fp.data)); | 277 memcpy(fp.data, decoded.data(), arraysize(fp.data)); |
290 fingerprints->push_back(fp); | 278 fingerprints->push_back(fp); |
291 return true; | 279 return true; |
292 } | 280 } |
293 | 281 |
294 // static | |
295 bool TransportSecurityState::GetPublicKeyHash( | |
296 const X509Certificate& cert, SHA1Fingerprint* spki_hash) { | |
297 std::string der_bytes; | |
298 if (!X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der_bytes)) | |
299 return false; | |
300 | |
301 base::StringPiece spki; | |
302 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) | |
303 return false; | |
304 | |
305 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki.data()), | |
306 spki.size(), spki_hash->data); | |
307 | |
308 return true; | |
309 } | |
310 | |
311 struct FingerprintsEqualPredicate { | 282 struct FingerprintsEqualPredicate { |
312 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : | 283 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : |
313 fingerprint_(fingerprint) {} | 284 fingerprint_(fingerprint) {} |
314 | 285 |
315 bool operator()(const SHA1Fingerprint& other) const { | 286 bool operator()(const SHA1Fingerprint& other) const { |
316 return fingerprint_.Equals(other); | 287 return fingerprint_.Equals(other); |
317 } | 288 } |
318 | 289 |
319 const SHA1Fingerprint& fingerprint_; | 290 const SHA1Fingerprint& fingerprint_; |
320 }; | 291 }; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
361 const FingerprintVector& from_cert_chain = ssl_info.public_key_hashes; | 332 const FingerprintVector& from_cert_chain = ssl_info.public_key_hashes; |
362 if (from_cert_chain.empty()) | 333 if (from_cert_chain.empty()) |
363 return false; | 334 return false; |
364 | 335 |
365 return IsBackupPinPresent(pins, from_cert_chain) && | 336 return IsBackupPinPresent(pins, from_cert_chain) && |
366 HashesIntersect(pins, from_cert_chain); | 337 HashesIntersect(pins, from_cert_chain); |
367 } | 338 } |
368 | 339 |
369 // "Public-Key-Pins" ":" | 340 // "Public-Key-Pins" ":" |
370 // "max-age" "=" delta-seconds ";" | 341 // "max-age" "=" delta-seconds ";" |
371 // "pin-" algo "=" base64 [ ";" ... ] | 342 // "pin-" algo "=" base64 [ ";" ... ] |
Ryan Sleevi
2012/03/28 00:50:32
Update the ABNF for -01.
palmer
2012/04/10 23:25:51
Done.
| |
372 // | 343 bool TransportSecurityState::DomainState::ParsePinsHeader( |
373 // static | 344 const base::Time& now, |
374 bool TransportSecurityState::ParsePinsHeader(const std::string& value, | 345 const std::string& value, |
375 const SSLInfo& ssl_info, | 346 const SSLInfo& ssl_info) { |
376 DomainState* state) { | |
377 bool parsed_max_age = false; | 347 bool parsed_max_age = false; |
378 int max_age = 0; | 348 int max_age_candidate = 0; |
379 FingerprintVector pins; | 349 FingerprintVector pins; |
380 | 350 |
381 std::string source = value; | 351 std::string source = value; |
382 | 352 |
383 while (!source.empty()) { | 353 while (!source.empty()) { |
384 StringPair semicolon = Split(source, ';'); | 354 StringPair semicolon = Split(source, ';'); |
385 semicolon.first = Strip(semicolon.first); | 355 semicolon.first = Strip(semicolon.first); |
386 semicolon.second = Strip(semicolon.second); | 356 semicolon.second = Strip(semicolon.second); |
387 StringPair equals = Split(semicolon.first, '='); | 357 StringPair equals = Split(semicolon.first, '='); |
388 equals.first = Strip(equals.first); | 358 equals.first = Strip(equals.first); |
389 equals.second = Strip(equals.second); | 359 equals.second = Strip(equals.second); |
390 | 360 |
391 if (LowerCaseEqualsASCII(equals.first, "max-age")) { | 361 if (LowerCaseEqualsASCII(equals.first, "max-age")) { |
392 if (equals.second.empty() || | 362 if (equals.second.empty() || |
393 !MaxAgeToInt(equals.second.begin(), equals.second.end(), &max_age)) { | 363 !MaxAgeToInt(equals.second.begin(), equals.second.end(), |
364 &max_age_candidate)) { | |
394 return false; | 365 return false; |
395 } | 366 } |
396 if (max_age > kMaxHSTSAgeSecs) | 367 if (max_age_candidate > kMaxHSTSAgeSecs) |
397 max_age = kMaxHSTSAgeSecs; | 368 max_age_candidate = kMaxHSTSAgeSecs; |
398 parsed_max_age = true; | 369 parsed_max_age = true; |
399 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { | 370 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { |
400 if (!ParseAndAppendPin(equals.second, &pins)) | 371 if (!ParseAndAppendPin(equals.second, &pins)) |
401 return false; | 372 return false; |
402 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | 373 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { |
403 // TODO(palmer) | 374 // TODO(palmer) |
404 } else { | 375 } else { |
405 // Silently ignore unknown directives for forward compatibility. | 376 // Silently ignore unknown directives for forward compatibility. |
406 } | 377 } |
407 | 378 |
408 source = semicolon.second; | 379 source = semicolon.second; |
409 } | 380 } |
410 | 381 |
411 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) | 382 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) |
412 return false; | 383 return false; |
413 | 384 |
414 state->max_age = max_age; | 385 dynamic_spki_hashes_expiry = |
415 state->dynamic_spki_hashes_expiry = | 386 now + base::TimeDelta::FromSeconds(max_age_candidate); |
416 base::Time::Now() + base::TimeDelta::FromSeconds(max_age); | |
417 | 387 |
418 state->dynamic_spki_hashes.clear(); | 388 dynamic_spki_hashes.clear(); |
419 if (max_age > 0) { | 389 if (max_age_candidate > 0) { |
420 for (FingerprintVector::const_iterator i = pins.begin(); | 390 for (FingerprintVector::const_iterator i = pins.begin(); |
421 i != pins.end(); i++) { | 391 i != pins.end(); ++i) { |
422 state->dynamic_spki_hashes.push_back(*i); | 392 dynamic_spki_hashes.push_back(*i); |
423 } | 393 } |
424 } | 394 } |
425 | 395 |
426 return true; | 396 return true; |
427 } | 397 } |
428 | 398 |
429 // "Strict-Transport-Security" ":" | 399 // "Strict-Transport-Security" ":" |
430 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] | 400 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] |
Ryan Sleevi
2012/03/28 00:50:32
Definitely update this guy
palmer
2012/04/10 23:25:51
What is wrong about this comment, other than being
| |
431 // | 401 bool TransportSecurityState::DomainState::ParseSTSHeader( |
432 // static | 402 const base::Time& now, |
433 bool TransportSecurityState::ParseHeader(const std::string& value, | 403 const std::string& value) { |
434 int* max_age, | |
435 bool* include_subdomains) { | |
436 DCHECK(max_age); | |
437 DCHECK(include_subdomains); | |
438 | |
439 int max_age_candidate = 0; | 404 int max_age_candidate = 0; |
440 | 405 |
441 enum ParserState { | 406 enum ParserState { |
442 START, | 407 START, |
443 AFTER_MAX_AGE_LABEL, | 408 AFTER_MAX_AGE_LABEL, |
444 AFTER_MAX_AGE_EQUALS, | 409 AFTER_MAX_AGE_EQUALS, |
445 AFTER_MAX_AGE, | 410 AFTER_MAX_AGE, |
446 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, | 411 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, |
447 AFTER_INCLUDE_SUBDOMAINS, | 412 AFTER_INCLUDE_SUBDOMAINS, |
448 } state = START; | 413 } state = START; |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
505 } | 470 } |
506 } | 471 } |
507 | 472 |
508 // We've consumed all the input. Let's see what state we ended up in. | 473 // We've consumed all the input. Let's see what state we ended up in. |
509 switch (state) { | 474 switch (state) { |
510 case START: | 475 case START: |
511 case AFTER_MAX_AGE_LABEL: | 476 case AFTER_MAX_AGE_LABEL: |
512 case AFTER_MAX_AGE_EQUALS: | 477 case AFTER_MAX_AGE_EQUALS: |
513 return false; | 478 return false; |
514 case AFTER_MAX_AGE: | 479 case AFTER_MAX_AGE: |
515 *max_age = max_age_candidate; | 480 upgrade_expiry = |
516 *include_subdomains = false; | 481 now + base::TimeDelta::FromSeconds(max_age_candidate); |
482 include_subdomains = false; | |
483 upgrade_mode = MODE_FORCE_HTTPS; | |
517 return true; | 484 return true; |
518 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: | 485 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: |
519 return false; | 486 return false; |
520 case AFTER_INCLUDE_SUBDOMAINS: | 487 case AFTER_INCLUDE_SUBDOMAINS: |
521 *max_age = max_age_candidate; | 488 upgrade_expiry = |
522 *include_subdomains = true; | 489 now + base::TimeDelta::FromSeconds(max_age_candidate); |
490 include_subdomains = true; | |
491 upgrade_mode = MODE_FORCE_HTTPS; | |
523 return true; | 492 return true; |
524 default: | 493 default: |
525 NOTREACHED(); | 494 NOTREACHED(); |
526 return false; | 495 return false; |
527 } | 496 } |
528 } | 497 } |
529 | 498 |
530 // Side pinning and superfluous certificates: | |
531 // | |
532 // In SSLClientSocketNSS::DoVerifyCertComplete we look for certificates with a | |
533 // Subject of CN=meta. When we find one we'll currently try and parse side | |
534 // pinned key from it. | |
535 // | |
536 // A side pin is a key which can be pinned to, but also can be kept offline and | |
537 // still held by the site owner. The CN=meta certificate is just a backwards | |
538 // compatiable method of carrying a lump of bytes to the client. (We could use | |
539 // a TLS extension just as well, but it's a lot easier for admins to add extra | |
540 // certificates to the chain.) | |
541 | |
542 // A TagMap represents the simple key-value structure that we use. Keys are | |
543 // 32-bit ints. Values are byte strings. | |
544 typedef std::map<uint32, base::StringPiece> TagMap; | |
545 | |
546 // ParseTags parses a list of key-value pairs from |in| to |out| and advances | |
547 // |in| past the data. The key-value pair data is: | |
548 // u16le num_tags | |
549 // u32le tag[num_tags] | |
550 // u16le lengths[num_tags] | |
551 // ...data... | |
552 static bool ParseTags(base::StringPiece* in, TagMap *out) { | |
553 // Many part of Chrome already assume little-endian. This is just to help | |
554 // anyone who should try to port it in the future. | |
555 #if defined(__BYTE_ORDER) | |
556 // Linux check | |
557 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian); | |
558 #elif defined(__BIG_ENDIAN__) | |
559 // Mac check | |
560 #error assumes little endian | |
561 #endif | |
562 | |
563 uint16 num_tags_16; | |
564 if (in->size() < sizeof(num_tags_16)) | |
565 return false; | |
566 | |
567 memcpy(&num_tags_16, in->data(), sizeof(num_tags_16)); | |
568 in->remove_prefix(sizeof(num_tags_16)); | |
569 unsigned num_tags = num_tags_16; | |
570 | |
571 if (in->size() < 6 * num_tags) | |
572 return false; | |
573 | |
574 const uint32* tags = reinterpret_cast<const uint32*>(in->data()); | |
575 const uint16* lens = reinterpret_cast<const uint16*>( | |
576 in->data() + 4*num_tags); | |
577 in->remove_prefix(6*num_tags); | |
578 | |
579 uint32 prev_tag = 0; | |
580 for (unsigned i = 0; i < num_tags; i++) { | |
581 size_t len = lens[i]; | |
582 uint32 tag = tags[i]; | |
583 | |
584 if (in->size() < len) | |
585 return false; | |
586 // tags must be in ascending order. | |
587 if (i > 0 && prev_tag >= tag) | |
588 return false; | |
589 (*out)[tag] = base::StringPiece(in->data(), len); | |
590 in->remove_prefix(len); | |
591 prev_tag = tag; | |
592 } | |
593 | |
594 return true; | |
595 } | |
596 | |
597 // GetTag extracts the data associated with |tag| in |tags|. | |
598 static bool GetTag(uint32 tag, const TagMap& tags, base::StringPiece* out) { | |
599 TagMap::const_iterator i = tags.find(tag); | |
600 if (i == tags.end()) | |
601 return false; | |
602 | |
603 *out = i->second; | |
604 return true; | |
605 } | |
606 | |
607 // kP256SubjectPublicKeyInfoPrefix can be prepended onto a P256 elliptic curve | |
608 // point in X9.62 format in order to make a valid SubjectPublicKeyInfo. The | |
609 // ASN.1 interpretation of these bytes is: | |
610 // | |
611 // 0:d=0 hl=2 l= 89 cons: SEQUENCE | |
612 // 2:d=1 hl=2 l= 19 cons: SEQUENCE | |
613 // 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey | |
614 // 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 | |
615 // 23:d=1 hl=2 l= 66 prim: BIT STRING | |
616 static const uint8 kP256SubjectPublicKeyInfoPrefix[] = { | |
617 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, | |
618 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, | |
619 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, | |
620 0x42, 0x00, | |
621 }; | |
622 | |
623 // VerifySignature returns true iff |sig| is a valid signature of | |
624 // |hash| by |pubkey|. The actual implementation is crypto library | |
625 // specific. | |
626 static bool VerifySignature(const base::StringPiece& pubkey, | |
627 const base::StringPiece& sig, | |
628 const base::StringPiece& hash); | |
629 | |
630 #if defined(USE_OPENSSL) | |
631 | |
632 static EVP_PKEY* DecodeX962P256PublicKey( | |
633 const base::StringPiece& pubkey_bytes) { | |
634 // The public key is an X9.62 encoded P256 point. | |
635 if (pubkey_bytes.size() != 1 + 2*32) | |
636 return NULL; | |
637 | |
638 std::string pubkey_spki( | |
639 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix), | |
640 sizeof(kP256SubjectPublicKeyInfoPrefix)); | |
641 pubkey_spki += pubkey_bytes.as_string(); | |
642 | |
643 EVP_PKEY* ret = NULL; | |
644 const unsigned char* der_pubkey = | |
645 reinterpret_cast<const unsigned char*>(pubkey_spki.data()); | |
646 d2i_PUBKEY(&ret, &der_pubkey, pubkey_spki.size()); | |
647 return ret; | |
648 } | |
649 | |
650 static bool VerifySignature(const base::StringPiece& pubkey, | |
651 const base::StringPiece& sig, | |
652 const base::StringPiece& hash) { | |
653 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> secpubkey( | |
654 DecodeX962P256PublicKey(pubkey)); | |
655 if (!secpubkey.get()) | |
656 return false; | |
657 | |
658 | |
659 crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ec_key( | |
660 EVP_PKEY_get1_EC_KEY(secpubkey.get())); | |
661 if (!ec_key.get()) | |
662 return false; | |
663 | |
664 return ECDSA_verify(0, reinterpret_cast<const unsigned char*>(hash.data()), | |
665 hash.size(), | |
666 reinterpret_cast<const unsigned char*>(sig.data()), | |
667 sig.size(), ec_key.get()) == 1; | |
668 } | |
669 | |
670 #else | |
671 | |
672 // DecodeX962P256PublicKey parses an uncompressed, X9.62 format, P256 elliptic | |
673 // curve point from |pubkey_bytes| and returns it as a SECKEYPublicKey. | |
674 static SECKEYPublicKey* DecodeX962P256PublicKey( | |
675 const base::StringPiece& pubkey_bytes) { | |
676 // The public key is an X9.62 encoded P256 point. | |
677 if (pubkey_bytes.size() != 1 + 2*32) | |
678 return NULL; | |
679 | |
680 std::string pubkey_spki( | |
681 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix), | |
682 sizeof(kP256SubjectPublicKeyInfoPrefix)); | |
683 pubkey_spki += pubkey_bytes.as_string(); | |
684 | |
685 SECItem der; | |
686 memset(&der, 0, sizeof(der)); | |
687 der.data = reinterpret_cast<uint8*>(const_cast<char*>(pubkey_spki.data())); | |
688 der.len = pubkey_spki.size(); | |
689 | |
690 CERTSubjectPublicKeyInfo* spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der); | |
691 if (!spki) | |
692 return NULL; | |
693 SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); | |
694 SECKEY_DestroySubjectPublicKeyInfo(spki); | |
695 | |
696 return public_key; | |
697 } | |
698 | |
699 static bool VerifySignature(const base::StringPiece& pubkey, | |
700 const base::StringPiece& sig, | |
701 const base::StringPiece& hash) { | |
702 SECKEYPublicKey* secpubkey = DecodeX962P256PublicKey(pubkey); | |
703 if (!secpubkey) | |
704 return false; | |
705 | |
706 SECItem sigitem; | |
707 memset(&sigitem, 0, sizeof(sigitem)); | |
708 sigitem.data = reinterpret_cast<uint8*>(const_cast<char*>(sig.data())); | |
709 sigitem.len = sig.size(); | |
710 | |
711 // |decoded_sigitem| is newly allocated, as is the data that it points to. | |
712 SECItem* decoded_sigitem = DSAU_DecodeDerSigToLen( | |
713 &sigitem, SECKEY_SignatureLen(secpubkey)); | |
714 | |
715 if (!decoded_sigitem) { | |
716 SECKEY_DestroyPublicKey(secpubkey); | |
717 return false; | |
718 } | |
719 | |
720 SECItem hashitem; | |
721 memset(&hashitem, 0, sizeof(hashitem)); | |
722 hashitem.data = reinterpret_cast<unsigned char*>( | |
723 const_cast<char*>(hash.data())); | |
724 hashitem.len = hash.size(); | |
725 | |
726 SECStatus rv = PK11_Verify(secpubkey, decoded_sigitem, &hashitem, NULL); | |
727 SECKEY_DestroyPublicKey(secpubkey); | |
728 SECITEM_FreeItem(decoded_sigitem, PR_TRUE); | |
729 return rv == SECSuccess; | |
730 } | |
731 | |
732 #endif // !defined(USE_OPENSSL) | |
733 | |
734 // These are the tag values that we use. Tags are little-endian on the wire and | |
735 // these values correspond to the ASCII of the name. | |
736 static const uint32 kTagALGO = 0x4f474c41; | |
737 static const uint32 kTagP256 = 0x36353250; | |
738 static const uint32 kTagPUBK = 0x4b425550; | |
739 static const uint32 kTagSIG = 0x474953; | |
740 static const uint32 kTagSPIN = 0x4e495053; | |
741 | |
742 // static | |
743 bool TransportSecurityState::ParseSidePin( | |
744 const base::StringPiece& leaf_spki, | |
745 const base::StringPiece& in_side_info, | |
746 FingerprintVector* out_pub_key_hash) { | |
747 base::StringPiece side_info(in_side_info); | |
748 | |
749 TagMap outer; | |
750 if (!ParseTags(&side_info, &outer)) | |
751 return false; | |
752 // trailing data is not allowed | |
753 if (side_info.size()) | |
754 return false; | |
755 | |
756 base::StringPiece side_pin_bytes; | |
757 if (!GetTag(kTagSPIN, outer, &side_pin_bytes)) | |
758 return false; | |
759 | |
760 bool have_parsed_a_key = false; | |
761 uint8 leaf_spki_hash[crypto::kSHA256Length]; | |
762 bool have_leaf_spki_hash = false; | |
763 | |
764 while (side_pin_bytes.size() > 0) { | |
765 TagMap side_pin; | |
766 if (!ParseTags(&side_pin_bytes, &side_pin)) | |
767 return false; | |
768 | |
769 base::StringPiece algo, pubkey, sig; | |
770 if (!GetTag(kTagALGO, side_pin, &algo) || | |
771 !GetTag(kTagPUBK, side_pin, &pubkey) || | |
772 !GetTag(kTagSIG, side_pin, &sig)) { | |
773 return false; | |
774 } | |
775 | |
776 if (algo.size() != sizeof(kTagP256) || | |
777 0 != memcmp(algo.data(), &kTagP256, sizeof(kTagP256))) { | |
778 // We don't support anything but P256 at the moment. | |
779 continue; | |
780 } | |
781 | |
782 if (!have_leaf_spki_hash) { | |
783 crypto::SHA256HashString( | |
784 leaf_spki.as_string(), leaf_spki_hash, sizeof(leaf_spki_hash)); | |
785 have_leaf_spki_hash = true; | |
786 } | |
787 | |
788 if (VerifySignature(pubkey, sig, base::StringPiece( | |
789 reinterpret_cast<const char*>(leaf_spki_hash), | |
790 sizeof(leaf_spki_hash)))) { | |
791 SHA1Fingerprint fpr; | |
792 base::SHA1HashBytes( | |
793 reinterpret_cast<const uint8*>(pubkey.data()), | |
794 pubkey.size(), | |
795 fpr.data); | |
796 out_pub_key_hash->push_back(fpr); | |
797 have_parsed_a_key = true; | |
798 } | |
799 } | |
800 | |
801 return have_parsed_a_key; | |
802 } | |
803 | |
804 // This function converts the binary hashes, which we store in | |
805 // |enabled_hosts_|, to a base64 string which we can include in a JSON file. | |
806 static std::string HashedDomainToExternalString(const std::string& hashed) { | |
807 std::string out; | |
808 CHECK(base::Base64Encode(hashed, &out)); | |
809 return out; | |
810 } | |
811 | |
812 // This inverts |HashedDomainToExternalString|, above. It turns an external | |
813 // string (from a JSON file) into an internal (binary) string. | |
814 static std::string ExternalStringToHashedDomain(const std::string& external) { | |
815 std::string out; | |
816 if (!base::Base64Decode(external, &out) || | |
817 out.size() != crypto::kSHA256Length) { | |
818 return std::string(); | |
819 } | |
820 | |
821 return out; | |
822 } | |
823 | |
824 static ListValue* SPKIHashesToListValue(const FingerprintVector& hashes) { | |
825 ListValue* pins = new ListValue; | |
826 | |
827 for (FingerprintVector::const_iterator i = hashes.begin(); | |
828 i != hashes.end(); ++i) { | |
829 std::string hash_str(reinterpret_cast<const char*>(i->data), | |
830 sizeof(i->data)); | |
831 std::string b64; | |
832 base::Base64Encode(hash_str, &b64); | |
833 pins->Append(new StringValue("sha1/" + b64)); | |
834 } | |
835 | |
836 return pins; | |
837 } | |
838 | |
839 bool TransportSecurityState::Serialise(std::string* output) { | |
840 DCHECK(CalledOnValidThread()); | |
841 | |
842 DictionaryValue toplevel; | |
843 base::Time now = base::Time::Now(); | |
844 for (std::map<std::string, DomainState>::const_iterator | |
845 i = enabled_hosts_.begin(); i != enabled_hosts_.end(); ++i) { | |
846 DictionaryValue* state = new DictionaryValue; | |
847 state->SetBoolean("include_subdomains", i->second.include_subdomains); | |
848 state->SetDouble("created", i->second.created.ToDoubleT()); | |
849 state->SetDouble("expiry", i->second.expiry.ToDoubleT()); | |
850 state->SetDouble("dynamic_spki_hashes_expiry", | |
851 i->second.dynamic_spki_hashes_expiry.ToDoubleT()); | |
852 | |
853 switch (i->second.mode) { | |
854 case DomainState::MODE_STRICT: | |
855 state->SetString("mode", "strict"); | |
856 break; | |
857 case DomainState::MODE_SPDY_ONLY: | |
858 state->SetString("mode", "spdy-only"); | |
859 break; | |
860 case DomainState::MODE_PINNING_ONLY: | |
861 state->SetString("mode", "pinning-only"); | |
862 break; | |
863 default: | |
864 NOTREACHED() << "DomainState with unknown mode"; | |
865 delete state; | |
866 continue; | |
867 } | |
868 | |
869 state->Set("preloaded_spki_hashes", | |
870 SPKIHashesToListValue(i->second.preloaded_spki_hashes)); | |
871 | |
872 if (now < i->second.dynamic_spki_hashes_expiry) { | |
873 state->Set("dynamic_spki_hashes", | |
874 SPKIHashesToListValue(i->second.dynamic_spki_hashes)); | |
875 } | |
876 | |
877 toplevel.Set(HashedDomainToExternalString(i->first), state); | |
878 } | |
879 | |
880 base::JSONWriter::WriteWithOptions(&toplevel, | |
881 base::JSONWriter::OPTIONS_PRETTY_PRINT, | |
882 output); | |
883 return true; | |
884 } | |
885 | |
886 bool TransportSecurityState::LoadEntries(const std::string& input, | |
887 bool* dirty) { | |
888 DCHECK(CalledOnValidThread()); | |
889 | |
890 enabled_hosts_.clear(); | |
891 return Deserialise(input, dirty, &enabled_hosts_); | |
892 } | |
893 | |
894 static bool AddHash(const std::string& type_and_base64, | 499 static bool AddHash(const std::string& type_and_base64, |
895 FingerprintVector* out) { | 500 FingerprintVector* out) { |
896 SHA1Fingerprint hash; | 501 SHA1Fingerprint hash; |
897 | 502 |
898 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | 503 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) |
899 return false; | 504 return false; |
900 | 505 |
901 out->push_back(hash); | 506 out->push_back(hash); |
902 return true; | 507 return true; |
903 } | 508 } |
904 | 509 |
905 static void SPKIHashesFromListValue(FingerprintVector* hashes, | |
906 const ListValue& pins) { | |
907 size_t num_pins = pins.GetSize(); | |
908 for (size_t i = 0; i < num_pins; ++i) { | |
909 std::string type_and_base64; | |
910 if (pins.GetString(i, &type_and_base64)) | |
911 AddHash(type_and_base64, hashes); | |
912 } | |
913 } | |
914 | |
915 // static | |
916 bool TransportSecurityState::Deserialise( | |
917 const std::string& input, | |
918 bool* dirty, | |
919 std::map<std::string, DomainState>* out) { | |
920 scoped_ptr<Value> value( | |
921 base::JSONReader::Read(input, false /* do not allow trailing commas */)); | |
922 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) | |
923 return false; | |
924 | |
925 DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get()); | |
926 const base::Time current_time(base::Time::Now()); | |
927 bool dirtied = false; | |
928 | |
929 for (DictionaryValue::key_iterator i = dict_value->begin_keys(); | |
930 i != dict_value->end_keys(); ++i) { | |
931 DictionaryValue* state; | |
932 if (!dict_value->GetDictionaryWithoutPathExpansion(*i, &state)) | |
933 continue; | |
934 | |
935 bool include_subdomains; | |
936 std::string mode_string; | |
937 double created; | |
938 double expiry; | |
939 double dynamic_spki_hashes_expiry = 0.0; | |
940 | |
941 if (!state->GetBoolean("include_subdomains", &include_subdomains) || | |
942 !state->GetString("mode", &mode_string) || | |
943 !state->GetDouble("expiry", &expiry)) { | |
944 continue; | |
945 } | |
946 | |
947 // Don't fail if this key is not present. | |
948 (void) state->GetDouble("dynamic_spki_hashes_expiry", | |
949 &dynamic_spki_hashes_expiry); | |
950 | |
951 ListValue* pins_list = NULL; | |
952 FingerprintVector preloaded_spki_hashes; | |
953 if (state->GetList("preloaded_spki_hashes", &pins_list)) | |
954 SPKIHashesFromListValue(&preloaded_spki_hashes, *pins_list); | |
955 | |
956 FingerprintVector dynamic_spki_hashes; | |
957 if (state->GetList("dynamic_spki_hashes", &pins_list)) | |
958 SPKIHashesFromListValue(&dynamic_spki_hashes, *pins_list); | |
959 | |
960 DomainState::Mode mode; | |
961 if (mode_string == "strict") { | |
962 mode = DomainState::MODE_STRICT; | |
963 } else if (mode_string == "spdy-only") { | |
964 mode = DomainState::MODE_SPDY_ONLY; | |
965 } else if (mode_string == "pinning-only") { | |
966 mode = DomainState::MODE_PINNING_ONLY; | |
967 } else { | |
968 LOG(WARNING) << "Unknown TransportSecurityState mode string found: " | |
969 << mode_string; | |
970 continue; | |
971 } | |
972 | |
973 base::Time expiry_time = base::Time::FromDoubleT(expiry); | |
974 base::Time dynamic_spki_hashes_expiry_time = | |
975 base::Time::FromDoubleT(dynamic_spki_hashes_expiry); | |
976 base::Time created_time; | |
977 if (state->GetDouble("created", &created)) { | |
978 created_time = base::Time::FromDoubleT(created); | |
979 } else { | |
980 // We're migrating an old entry with no creation date. Make sure we | |
981 // write the new date back in a reasonable time frame. | |
982 dirtied = true; | |
983 created_time = base::Time::Now(); | |
984 } | |
985 | |
986 if (expiry_time <= current_time && | |
987 dynamic_spki_hashes_expiry_time <= current_time) { | |
988 // Make sure we dirty the state if we drop an entry. | |
989 dirtied = true; | |
990 continue; | |
991 } | |
992 | |
993 std::string hashed = ExternalStringToHashedDomain(*i); | |
994 if (hashed.empty()) { | |
995 dirtied = true; | |
996 continue; | |
997 } | |
998 | |
999 DomainState new_state; | |
1000 new_state.mode = mode; | |
1001 new_state.created = created_time; | |
1002 new_state.expiry = expiry_time; | |
1003 new_state.include_subdomains = include_subdomains; | |
1004 new_state.preloaded_spki_hashes = preloaded_spki_hashes; | |
1005 new_state.dynamic_spki_hashes = dynamic_spki_hashes; | |
1006 new_state.dynamic_spki_hashes_expiry = dynamic_spki_hashes_expiry_time; | |
1007 (*out)[hashed] = new_state; | |
1008 } | |
1009 | |
1010 *dirty = dirtied; | |
1011 return true; | |
1012 } | |
1013 | |
1014 TransportSecurityState::~TransportSecurityState() { | 510 TransportSecurityState::~TransportSecurityState() { |
1015 } | 511 } |
1016 | 512 |
1017 void TransportSecurityState::DirtyNotify() { | 513 void TransportSecurityState::DirtyNotify() { |
1018 DCHECK(CalledOnValidThread()); | 514 DCHECK(CalledOnValidThread()); |
1019 | 515 |
1020 if (delegate_) | 516 if (delegate_) |
1021 delegate_->StateIsDirty(this); | 517 delegate_->StateIsDirty(this); |
1022 } | 518 } |
1023 | 519 |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1117 for (size_t j = 0; j < num_entries; j++) { | 613 for (size_t j = 0; j < num_entries; j++) { |
1118 if (entries[j].length == canonicalized_host.size() - i && | 614 if (entries[j].length == canonicalized_host.size() - i && |
1119 memcmp(entries[j].dns_name, &canonicalized_host[i], | 615 memcmp(entries[j].dns_name, &canonicalized_host[i], |
1120 entries[j].length) == 0) { | 616 entries[j].length) == 0) { |
1121 if (!entries[j].include_subdomains && i != 0) { | 617 if (!entries[j].include_subdomains && i != 0) { |
1122 *ret = false; | 618 *ret = false; |
1123 } else { | 619 } else { |
1124 out->include_subdomains = entries[j].include_subdomains; | 620 out->include_subdomains = entries[j].include_subdomains; |
1125 *ret = true; | 621 *ret = true; |
1126 if (!entries[j].https_required) | 622 if (!entries[j].https_required) |
1127 out->mode = TransportSecurityState::DomainState::MODE_PINNING_ONLY; | 623 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; |
1128 if (entries[j].pins.required_hashes) { | 624 if (entries[j].pins.required_hashes) { |
1129 const char* const* hash = entries[j].pins.required_hashes; | 625 const char* const* hash = entries[j].pins.required_hashes; |
1130 while (*hash) { | 626 while (*hash) { |
1131 bool ok = AddHash(*hash, &out->preloaded_spki_hashes); | 627 bool ok = AddHash(*hash, &out->static_spki_hashes); |
1132 DCHECK(ok) << " failed to parse " << *hash; | 628 DCHECK(ok) << " failed to parse " << *hash; |
1133 hash++; | 629 hash++; |
1134 } | 630 } |
1135 } | 631 } |
1136 if (entries[j].pins.excluded_hashes) { | 632 if (entries[j].pins.excluded_hashes) { |
1137 const char* const* hash = entries[j].pins.excluded_hashes; | 633 const char* const* hash = entries[j].pins.excluded_hashes; |
1138 while (*hash) { | 634 while (*hash) { |
1139 bool ok = AddHash(*hash, &out->bad_preloaded_spki_hashes); | 635 bool ok = AddHash(*hash, &out->bad_static_spki_hashes); |
1140 DCHECK(ok) << " failed to parse " << *hash; | 636 DCHECK(ok) << " failed to parse " << *hash; |
1141 hash++; | 637 hash++; |
1142 } | 638 } |
1143 } | 639 } |
1144 } | 640 } |
1145 return true; | 641 return true; |
1146 } | 642 } |
1147 } | 643 } |
1148 return false; | 644 return false; |
1149 } | 645 } |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1253 kSPKIHash_UTNUSERFirstHardware, | 749 kSPKIHash_UTNUSERFirstHardware, |
1254 kSPKIHash_UTNUSERFirstObject, | 750 kSPKIHash_UTNUSERFirstObject, |
1255 kSPKIHash_GTECyberTrustGlobalRoot, | 751 kSPKIHash_GTECyberTrustGlobalRoot, |
1256 NULL, | 752 NULL, |
1257 }; | 753 }; |
1258 #define kTwitterCDNPins { \ | 754 #define kTwitterCDNPins { \ |
1259 kTwitterCDNAcceptableCerts, \ | 755 kTwitterCDNAcceptableCerts, \ |
1260 kNoRejectedPublicKeys, \ | 756 kNoRejectedPublicKeys, \ |
1261 } | 757 } |
1262 | 758 |
759 // These are the tag values that we use. Tags are little-endian on the wire and | |
760 // these values correspond to the ASCII of the name. | |
761 static const uint32 kTagALGO = 0x4f474c41; | |
762 static const uint32 kTagP256 = 0x36353250; | |
763 static const uint32 kTagPUBK = 0x4b425550; | |
764 static const uint32 kTagSIG = 0x474953; | |
765 static const uint32 kTagSPIN = 0x4e495053; | |
Ryan Sleevi
2012/03/28 00:50:32
This was related to side pinning - delete?
palmer
2012/04/10 23:25:51
Done.
| |
766 | |
1263 // kTestAcceptableCerts doesn't actually match any public keys and is used | 767 // kTestAcceptableCerts doesn't actually match any public keys and is used |
1264 // with "pinningtest.appspot.com", below, to test if pinning is active. | 768 // with "pinningtest.appspot.com", below, to test if pinning is active. |
1265 static const char* const kTestAcceptableCerts[] = { | 769 static const char* const kTestAcceptableCerts[] = { |
1266 "sha1/AAAAAAAAAAAAAAAAAAAAAAAAAAA=", | 770 "sha1/AAAAAAAAAAAAAAAAAAAAAAAAAAA=", |
1267 NULL, | 771 NULL, |
1268 }; | 772 }; |
1269 #define kTestPins { \ | 773 #define kTestPins { \ |
1270 kTestAcceptableCerts, \ | 774 kTestAcceptableCerts, \ |
1271 kNoRejectedPublicKeys, \ | 775 kNoRejectedPublicKeys, \ |
1272 } | 776 } |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1514 return entry; | 1018 return entry; |
1515 } | 1019 } |
1516 } | 1020 } |
1517 } | 1021 } |
1518 | 1022 |
1519 return NULL; | 1023 return NULL; |
1520 } | 1024 } |
1521 | 1025 |
1522 // static | 1026 // static |
1523 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | 1027 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, |
1524 bool sni_available) { | 1028 bool sni_enabled) { |
1525 std::string canonicalized_host = CanonicalizeHost(host); | 1029 std::string canonicalized_host = CanonicalizeHost(host); |
1526 const struct HSTSPreload* entry = | 1030 const struct HSTSPreload* entry = |
1527 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 1031 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
1528 | 1032 |
1529 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 1033 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
1530 return true; | 1034 return true; |
1531 | 1035 |
1532 if (sni_available) { | 1036 if (sni_enabled) { |
1533 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 1037 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, |
1534 kNumPreloadedSNISTS); | 1038 kNumPreloadedSNISTS); |
1535 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 1039 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
1536 return true; | 1040 return true; |
1537 } | 1041 } |
1538 | 1042 |
1539 return false; | 1043 return false; |
1540 } | 1044 } |
1541 | 1045 |
1542 // static | 1046 // static |
1543 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | 1047 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { |
1544 std::string canonicalized_host = CanonicalizeHost(host); | 1048 std::string canonicalized_host = CanonicalizeHost(host); |
1545 | 1049 |
1546 const struct HSTSPreload* entry = | 1050 const struct HSTSPreload* entry = |
1547 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 1051 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
1548 | 1052 |
1549 if (!entry) { | 1053 if (!entry) { |
1550 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 1054 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, |
1551 kNumPreloadedSNISTS); | 1055 kNumPreloadedSNISTS); |
1552 } | 1056 } |
1553 | 1057 |
1554 DCHECK(entry); | 1058 DCHECK(entry); |
1555 DCHECK(entry->pins.required_hashes); | 1059 DCHECK(entry->pins.required_hashes); |
1556 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | 1060 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); |
1557 | 1061 |
1558 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | 1062 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
1559 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | 1063 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); |
1560 } | 1064 } |
1561 | 1065 |
1562 // IsPreloadedSTS returns true if the canonicalized hostname should always be | 1066 bool TransportSecurityState::GetStaticDomainState( |
1563 // considered to have STS enabled. | |
1564 bool TransportSecurityState::IsPreloadedSTS( | |
1565 const std::string& canonicalized_host, | 1067 const std::string& canonicalized_host, |
1566 bool sni_available, | 1068 bool sni_enabled, |
1567 DomainState* out) { | 1069 DomainState* out) { |
1568 DCHECK(CalledOnValidThread()); | 1070 DCHECK(CalledOnValidThread()); |
1569 | 1071 |
1570 out->preloaded = true; | 1072 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
1571 out->mode = DomainState::MODE_STRICT; | |
1572 out->include_subdomains = false; | 1073 out->include_subdomains = false; |
1573 | 1074 |
1574 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 1075 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
1575 std::string host_sub_chunk(&canonicalized_host[i], | 1076 std::string host_sub_chunk(&canonicalized_host[i], |
1576 canonicalized_host.size() - i); | 1077 canonicalized_host.size() - i); |
1577 out->domain = DNSDomainToString(host_sub_chunk); | 1078 out->domain = DNSDomainToString(host_sub_chunk); |
1578 std::string hashed_host(HashHost(host_sub_chunk)); | 1079 std::string hashed_host(HashHost(host_sub_chunk)); |
1579 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { | 1080 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { |
1580 *out = forced_hosts_[hashed_host]; | 1081 *out = forced_hosts_[hashed_host]; |
1581 out->domain = DNSDomainToString(host_sub_chunk); | 1082 out->domain = DNSDomainToString(host_sub_chunk); |
1582 out->preloaded = true; | |
1583 return true; | 1083 return true; |
1584 } | 1084 } |
1585 bool ret; | 1085 bool ret; |
1586 if (HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, | 1086 if (HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, |
1587 &ret)) { | 1087 &ret)) { |
1588 return ret; | 1088 return ret; |
1589 } | 1089 } |
1590 if (sni_available && | 1090 if (sni_enabled && |
1591 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, | 1091 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, |
1592 out, &ret)) { | 1092 out, &ret)) { |
1593 return ret; | 1093 return ret; |
1594 } | 1094 } |
1595 } | 1095 } |
1596 | 1096 |
1597 return false; | 1097 return false; |
1598 } | 1098 } |
1599 | 1099 |
1600 static std::string HashesToBase64String( | 1100 static std::string HashesToBase64String( |
1601 const FingerprintVector& hashes) { | 1101 const FingerprintVector& hashes) { |
1602 std::vector<std::string> hashes_strs; | 1102 std::vector<std::string> hashes_strs; |
1603 for (FingerprintVector::const_iterator | 1103 for (FingerprintVector::const_iterator |
1604 i = hashes.begin(); i != hashes.end(); i++) { | 1104 i = hashes.begin(); i != hashes.end(); i++) { |
1605 std::string s; | 1105 std::string s; |
1606 const std::string hash_str(reinterpret_cast<const char*>(i->data), | 1106 const std::string hash_str(reinterpret_cast<const char*>(i->data), |
1607 sizeof(i->data)); | 1107 sizeof(i->data)); |
1608 base::Base64Encode(hash_str, &s); | 1108 base::Base64Encode(hash_str, &s); |
1609 hashes_strs.push_back(s); | 1109 hashes_strs.push_back(s); |
1610 } | 1110 } |
1611 | 1111 |
1612 return JoinString(hashes_strs, ','); | 1112 return JoinString(hashes_strs, ','); |
1613 } | 1113 } |
1614 | 1114 |
1615 TransportSecurityState::DomainState::DomainState() | 1115 TransportSecurityState::DomainState::DomainState() |
1616 : mode(MODE_STRICT), | 1116 : upgrade_mode(MODE_FORCE_HTTPS), |
1617 created(base::Time::Now()), | 1117 created(base::Time::Now()), |
1618 include_subdomains(false), | 1118 include_subdomains(false) { |
1619 preloaded(false) { | |
1620 } | 1119 } |
1621 | 1120 |
1622 TransportSecurityState::DomainState::~DomainState() { | 1121 TransportSecurityState::DomainState::~DomainState() { |
1623 } | 1122 } |
1624 | 1123 |
1625 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( | 1124 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
1626 const FingerprintVector& hashes) { | 1125 const FingerprintVector& hashes) const { |
1627 | 1126 if (HashesIntersect(bad_static_spki_hashes, hashes)) { |
1628 if (HashesIntersect(bad_preloaded_spki_hashes, hashes)) { | |
1629 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 1127 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
1630 << ". Validated chain: " << HashesToBase64String(hashes) | 1128 << ". Validated chain: " << HashesToBase64String(hashes) |
1631 << ", matches one or more bad hashes: " | 1129 << ", matches one or more bad hashes: " |
1632 << HashesToBase64String(bad_preloaded_spki_hashes); | 1130 << HashesToBase64String(bad_static_spki_hashes); |
1633 return false; | 1131 return false; |
1634 } | 1132 } |
1635 | 1133 |
1636 if (!(dynamic_spki_hashes.empty() && preloaded_spki_hashes.empty()) && | 1134 if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) && |
1637 !HashesIntersect(dynamic_spki_hashes, hashes) && | 1135 !HashesIntersect(dynamic_spki_hashes, hashes) && |
1638 !HashesIntersect(preloaded_spki_hashes, hashes)) { | 1136 !HashesIntersect(static_spki_hashes, hashes)) { |
1639 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 1137 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
1640 << ". Validated chain: " << HashesToBase64String(hashes) | 1138 << ". Validated chain: " << HashesToBase64String(hashes) |
1641 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) | 1139 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) |
1642 << " or: " << HashesToBase64String(preloaded_spki_hashes); | 1140 << " or: " << HashesToBase64String(static_spki_hashes); |
1643 | 1141 |
1644 return false; | 1142 return false; |
1645 } | 1143 } |
1646 | 1144 |
1647 return true; | 1145 return true; |
1648 } | 1146 } |
1649 | 1147 |
1650 bool TransportSecurityState::DomainState::IsMoreStrict( | |
1651 const TransportSecurityState::DomainState& other) { | |
1652 if (this->dynamic_spki_hashes.empty() && !other.dynamic_spki_hashes.empty()) | |
1653 return false; | |
1654 | |
1655 if (!this->include_subdomains && other.include_subdomains) | |
1656 return false; | |
1657 | |
1658 return true; | |
1659 } | |
1660 | |
1661 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() | 1148 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() |
1662 const { | 1149 const { |
1663 return mode == MODE_STRICT; | 1150 return upgrade_mode == MODE_FORCE_HTTPS; |
1664 } | 1151 } |
1665 | 1152 |
1666 } // namespace | 1153 } // namespace |
OLD | NEW |