OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/transport_security_state.h" | 5 #include "net/base/transport_security_state.h" |
6 | 6 |
7 #if defined(USE_OPENSSL) | 7 #if defined(USE_OPENSSL) |
8 #include <openssl/ecdsa.h> | 8 #include <openssl/ecdsa.h> |
9 #include <openssl/ssl.h> | 9 #include <openssl/ssl.h> |
10 #else // !defined(USE_OPENSSL) | 10 #else // !defined(USE_OPENSSL) |
11 #include <cryptohi.h> | 11 #include <cryptohi.h> |
12 #include <hasht.h> | 12 #include <hasht.h> |
13 #include <keyhi.h> | 13 #include <keyhi.h> |
14 #include <pk11pub.h> | 14 #include <pk11pub.h> |
15 #include <nspr.h> | 15 #include <nspr.h> |
16 #endif | 16 #endif |
17 | 17 |
18 #include <algorithm> | 18 #include <algorithm> |
19 | 19 |
20 #include "base/base64.h" | 20 #include "base/base64.h" |
21 #include "base/build_time.h" | 21 #include "base/build_time.h" |
22 #include "base/logging.h" | 22 #include "base/logging.h" |
23 #include "base/memory/scoped_ptr.h" | 23 #include "base/memory/scoped_ptr.h" |
24 #include "base/metrics/histogram.h" | 24 #include "base/metrics/histogram.h" |
25 #include "base/sha1.h" | 25 #include "base/sha1.h" |
26 #include "base/string_number_conversions.h" | 26 #include "base/string_number_conversions.h" |
27 #include "base/string_tokenizer.h" | |
28 #include "base/string_util.h" | 27 #include "base/string_util.h" |
29 #include "base/time.h" | 28 #include "base/time.h" |
30 #include "base/utf_string_conversions.h" | 29 #include "base/utf_string_conversions.h" |
31 #include "base/values.h" | 30 #include "base/values.h" |
32 #include "crypto/sha2.h" | 31 #include "crypto/sha2.h" |
33 #include "googleurl/src/gurl.h" | 32 #include "googleurl/src/gurl.h" |
34 #include "net/base/dns_util.h" | 33 #include "net/base/dns_util.h" |
35 #include "net/base/ssl_info.h" | 34 #include "net/base/ssl_info.h" |
36 #include "net/base/x509_cert_types.h" | 35 #include "net/base/x509_cert_types.h" |
37 #include "net/base/x509_certificate.h" | 36 #include "net/base/x509_certificate.h" |
38 #include "net/http/http_util.h" | 37 #include "net/http/http_security_headers.h" |
39 | 38 |
40 #if defined(USE_OPENSSL) | 39 #if defined(USE_OPENSSL) |
41 #include "crypto/openssl_util.h" | 40 #include "crypto/openssl_util.h" |
42 #endif | 41 #endif |
43 | 42 |
44 namespace net { | 43 namespace net { |
45 | 44 |
46 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year | 45 namespace { |
47 | 46 |
48 static std::string HashHost(const std::string& canonicalized_host) { | 47 std::string HashesToBase64String(const HashValueVector& hashes) { |
| 48 std::string str; |
| 49 for (size_t i = 0; i != hashes.size(); ++i) { |
| 50 if (i != 0) |
| 51 str += ","; |
| 52 str += hashes[i].ToString(); |
| 53 } |
| 54 return str; |
| 55 } |
| 56 |
| 57 std::string HashHost(const std::string& canonicalized_host) { |
49 char hashed[crypto::kSHA256Length]; | 58 char hashed[crypto::kSHA256Length]; |
50 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | 59 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); |
51 return std::string(hashed, sizeof(hashed)); | 60 return std::string(hashed, sizeof(hashed)); |
52 } | 61 } |
53 | 62 |
| 63 // Returns true if the intersection of |a| and |b| is not empty. If either |
| 64 // |a| or |b| is empty, returns false. |
| 65 bool HashesIntersect(const HashValueVector& a, |
| 66 const HashValueVector& b) { |
| 67 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { |
| 68 HashValueVector::const_iterator j = |
| 69 std::find_if(b.begin(), b.end(), HashValuesEqual(*i)); |
| 70 if (j != b.end()) |
| 71 return true; |
| 72 } |
| 73 return false; |
| 74 } |
| 75 |
| 76 bool AddHash(const char* sha1_hash, |
| 77 HashValueVector* out) { |
| 78 HashValue hash(HASH_VALUE_SHA1); |
| 79 memcpy(hash.data(), sha1_hash, hash.size()); |
| 80 out->push_back(hash); |
| 81 return true; |
| 82 } |
| 83 |
| 84 } // namespace |
| 85 |
54 TransportSecurityState::TransportSecurityState() | 86 TransportSecurityState::TransportSecurityState() |
55 : delegate_(NULL) { | 87 : delegate_(NULL) { |
56 } | 88 } |
57 | 89 |
58 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state) | 90 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state) |
59 : iterator_(state.enabled_hosts_.begin()), | 91 : iterator_(state.enabled_hosts_.begin()), |
60 end_(state.enabled_hosts_.end()) { | 92 end_(state.enabled_hosts_.end()) { |
61 } | 93 } |
62 | 94 |
63 TransportSecurityState::Iterator::~Iterator() {} | 95 TransportSecurityState::Iterator::~Iterator() {} |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
176 enabled_hosts_.erase(i++); | 208 enabled_hosts_.erase(i++); |
177 } else { | 209 } else { |
178 i++; | 210 i++; |
179 } | 211 } |
180 } | 212 } |
181 | 213 |
182 if (dirtied) | 214 if (dirtied) |
183 DirtyNotify(); | 215 DirtyNotify(); |
184 } | 216 } |
185 | 217 |
186 // MaxAgeToInt converts a string representation of a number of seconds into a | |
187 // int. We use strtol in order to handle overflow correctly. The string may | |
188 // contain an arbitary number which we should truncate correctly rather than | |
189 // throwing a parse failure. | |
190 static bool MaxAgeToInt(std::string::const_iterator begin, | |
191 std::string::const_iterator end, | |
192 int* result) { | |
193 const std::string s(begin, end); | |
194 char* endptr; | |
195 long int i = strtol(s.data(), &endptr, 10 /* base */); | |
196 if (*endptr || i < 0) | |
197 return false; | |
198 if (i > TransportSecurityState::kMaxHSTSAgeSecs) | |
199 i = TransportSecurityState::kMaxHSTSAgeSecs; | |
200 *result = i; | |
201 return true; | |
202 } | |
203 | |
204 // Strip, Split, StringPair, and ParsePins are private implementation details | |
205 // of ParsePinsHeader(std::string&, DomainState&). | |
206 static std::string Strip(const std::string& source) { | |
207 if (source.empty()) | |
208 return source; | |
209 | |
210 std::string::const_iterator start = source.begin(); | |
211 std::string::const_iterator end = source.end(); | |
212 HttpUtil::TrimLWS(&start, &end); | |
213 return std::string(start, end); | |
214 } | |
215 | |
216 typedef std::pair<std::string, std::string> StringPair; | |
217 | |
218 static StringPair Split(const std::string& source, char delimiter) { | |
219 StringPair pair; | |
220 size_t point = source.find(delimiter); | |
221 | |
222 pair.first = source.substr(0, point); | |
223 if (std::string::npos != point) | |
224 pair.second = source.substr(point + 1); | |
225 | |
226 return pair; | |
227 } | |
228 | |
229 // static | |
230 bool TransportSecurityState::ParsePin(const std::string& value, | |
231 HashValue* out) { | |
232 StringPair slash = Split(Strip(value), '/'); | |
233 | |
234 if (slash.first == "sha1") | |
235 out->tag = HASH_VALUE_SHA1; | |
236 else if (slash.first == "sha256") | |
237 out->tag = HASH_VALUE_SHA256; | |
238 else | |
239 return false; | |
240 | |
241 std::string decoded; | |
242 if (!base::Base64Decode(slash.second, &decoded) || | |
243 decoded.size() != out->size()) { | |
244 return false; | |
245 } | |
246 | |
247 memcpy(out->data(), decoded.data(), out->size()); | |
248 return true; | |
249 } | |
250 | |
251 static bool ParseAndAppendPin(const std::string& value, | |
252 HashValueTag tag, | |
253 HashValueVector* hashes) { | |
254 std::string unquoted = HttpUtil::Unquote(value); | |
255 std::string decoded; | |
256 | |
257 // This code has to assume that 32 bytes is SHA-256 and 20 bytes is SHA-1. | |
258 // Currently, those are the only two possibilities, so the assumption is | |
259 // valid. | |
260 if (!base::Base64Decode(unquoted, &decoded)) | |
261 return false; | |
262 | |
263 HashValue hash(tag); | |
264 if (decoded.size() != hash.size()) | |
265 return false; | |
266 | |
267 memcpy(hash.data(), decoded.data(), hash.size()); | |
268 hashes->push_back(hash); | |
269 return true; | |
270 } | |
271 | |
272 struct HashValuesEqualPredicate { | |
273 explicit HashValuesEqualPredicate(const HashValue& fingerprint) : | |
274 fingerprint_(fingerprint) {} | |
275 | |
276 bool operator()(const HashValue& other) const { | |
277 return fingerprint_.Equals(other); | |
278 } | |
279 | |
280 const HashValue& fingerprint_; | |
281 }; | |
282 | |
283 // Returns true iff there is an item in |pins| which is not present in | |
284 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". | |
285 static bool IsBackupPinPresent(const HashValueVector& pins, | |
286 const HashValueVector& from_cert_chain) { | |
287 for (HashValueVector::const_iterator | |
288 i = pins.begin(); i != pins.end(); ++i) { | |
289 HashValueVector::const_iterator j = | |
290 std::find_if(from_cert_chain.begin(), from_cert_chain.end(), | |
291 HashValuesEqualPredicate(*i)); | |
292 if (j == from_cert_chain.end()) | |
293 return true; | |
294 } | |
295 | |
296 return false; | |
297 } | |
298 | |
299 // Returns true if the intersection of |a| and |b| is not empty. If either | |
300 // |a| or |b| is empty, returns false. | |
301 static bool HashesIntersect(const HashValueVector& a, | |
302 const HashValueVector& b) { | |
303 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { | |
304 HashValueVector::const_iterator j = | |
305 std::find_if(b.begin(), b.end(), HashValuesEqualPredicate(*i)); | |
306 if (j != b.end()) | |
307 return true; | |
308 } | |
309 | |
310 return false; | |
311 } | |
312 | |
313 // Returns true iff |pins| contains both a live and a backup pin. A live pin | |
314 // is a pin whose SPKI is present in the certificate chain in |ssl_info|. A | |
315 // backup pin is a pin intended for disaster recovery, not day-to-day use, and | |
316 // thus must be absent from the certificate chain. The Public-Key-Pins header | |
317 // specification requires both. | |
318 static bool IsPinListValid(const HashValueVector& pins, | |
319 const SSLInfo& ssl_info) { | |
320 // Fast fail: 1 live + 1 backup = at least 2 pins. (Check for actual | |
321 // liveness and backupness below.) | |
322 if (pins.size() < 2) | |
323 return false; | |
324 | |
325 const HashValueVector& from_cert_chain = ssl_info.public_key_hashes; | |
326 if (from_cert_chain.empty()) | |
327 return false; | |
328 | |
329 return IsBackupPinPresent(pins, from_cert_chain) && | |
330 HashesIntersect(pins, from_cert_chain); | |
331 } | |
332 | |
333 // "Public-Key-Pins" ":" | |
334 // "max-age" "=" delta-seconds ";" | |
335 // "pin-" algo "=" base64 [ ";" ... ] | |
336 bool TransportSecurityState::DomainState::ParsePinsHeader( | |
337 const base::Time& now, | |
338 const std::string& value, | |
339 const SSLInfo& ssl_info) { | |
340 bool parsed_max_age = false; | |
341 int max_age_candidate = 0; | |
342 HashValueVector pins; | |
343 | |
344 std::string source = value; | |
345 | |
346 while (!source.empty()) { | |
347 StringPair semicolon = Split(source, ';'); | |
348 semicolon.first = Strip(semicolon.first); | |
349 semicolon.second = Strip(semicolon.second); | |
350 StringPair equals = Split(semicolon.first, '='); | |
351 equals.first = Strip(equals.first); | |
352 equals.second = Strip(equals.second); | |
353 | |
354 if (LowerCaseEqualsASCII(equals.first, "max-age")) { | |
355 if (equals.second.empty() || | |
356 !MaxAgeToInt(equals.second.begin(), equals.second.end(), | |
357 &max_age_candidate)) { | |
358 return false; | |
359 } | |
360 if (max_age_candidate > kMaxHSTSAgeSecs) | |
361 max_age_candidate = kMaxHSTSAgeSecs; | |
362 parsed_max_age = true; | |
363 } else if (StartsWithASCII(equals.first, "pin-", false)) { | |
364 HashValueTag tag; | |
365 if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { | |
366 tag = HASH_VALUE_SHA1; | |
367 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | |
368 tag = HASH_VALUE_SHA256; | |
369 } else { | |
370 LOG(WARNING) << "Ignoring pin of unknown type: " << equals.first; | |
371 return false; | |
372 } | |
373 if (!ParseAndAppendPin(equals.second, tag, &pins)) | |
374 return false; | |
375 } else { | |
376 // Silently ignore unknown directives for forward compatibility. | |
377 } | |
378 | |
379 source = semicolon.second; | |
380 } | |
381 | |
382 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) | |
383 return false; | |
384 | |
385 dynamic_spki_hashes_expiry = | |
386 now + base::TimeDelta::FromSeconds(max_age_candidate); | |
387 | |
388 dynamic_spki_hashes.clear(); | |
389 if (max_age_candidate > 0) { | |
390 for (HashValueVector::const_iterator i = pins.begin(); | |
391 i != pins.end(); ++i) { | |
392 dynamic_spki_hashes.push_back(*i); | |
393 } | |
394 } | |
395 | |
396 return true; | |
397 } | |
398 | |
399 // Parse the Strict-Transport-Security header, as currently defined in | |
400 // http://tools.ietf.org/html/draft-ietf-websec-strict-transport-sec-14: | |
401 // | |
402 // Strict-Transport-Security = "Strict-Transport-Security" ":" | |
403 // [ directive ] *( ";" [ directive ] ) | |
404 // | |
405 // directive = directive-name [ "=" directive-value ] | |
406 // directive-name = token | |
407 // directive-value = token | quoted-string | |
408 // | |
409 // 1. The order of appearance of directives is not significant. | |
410 // | |
411 // 2. All directives MUST appear only once in an STS header field. | |
412 // Directives are either optional or required, as stipulated in | |
413 // their definitions. | |
414 // | |
415 // 3. Directive names are case-insensitive. | |
416 // | |
417 // 4. UAs MUST ignore any STS header fields containing directives, or | |
418 // other header field value data, that does not conform to the | |
419 // syntax defined in this specification. | |
420 // | |
421 // 5. If an STS header field contains directive(s) not recognized by | |
422 // the UA, the UA MUST ignore the unrecognized directives and if the | |
423 // STS header field otherwise satisfies the above requirements (1 | |
424 // through 4), the UA MUST process the recognized directives. | |
425 bool TransportSecurityState::DomainState::ParseSTSHeader( | |
426 const base::Time& now, | |
427 const std::string& value) { | |
428 int max_age_candidate = 0; | |
429 bool include_subdomains_candidate = false; | |
430 | |
431 // We must see max-age exactly once. | |
432 int max_age_observed = 0; | |
433 // We must see includeSubdomains exactly 0 or 1 times. | |
434 int include_subdomains_observed = 0; | |
435 | |
436 enum ParserState { | |
437 START, | |
438 AFTER_MAX_AGE_LABEL, | |
439 AFTER_MAX_AGE_EQUALS, | |
440 AFTER_MAX_AGE, | |
441 AFTER_INCLUDE_SUBDOMAINS, | |
442 AFTER_UNKNOWN_LABEL, | |
443 DIRECTIVE_END | |
444 } state = START; | |
445 | |
446 StringTokenizer tokenizer(value, " \t=;"); | |
447 tokenizer.set_options(StringTokenizer::RETURN_DELIMS); | |
448 tokenizer.set_quote_chars("\""); | |
449 std::string unquoted; | |
450 while (tokenizer.GetNext()) { | |
451 DCHECK(!tokenizer.token_is_delim() || tokenizer.token().length() == 1); | |
452 switch (state) { | |
453 case START: | |
454 case DIRECTIVE_END: | |
455 if (IsAsciiWhitespace(*tokenizer.token_begin())) | |
456 continue; | |
457 if (LowerCaseEqualsASCII(tokenizer.token(), "max-age")) { | |
458 state = AFTER_MAX_AGE_LABEL; | |
459 max_age_observed++; | |
460 } else if (LowerCaseEqualsASCII(tokenizer.token(), | |
461 "includesubdomains")) { | |
462 state = AFTER_INCLUDE_SUBDOMAINS; | |
463 include_subdomains_observed++; | |
464 include_subdomains_candidate = true; | |
465 } else { | |
466 state = AFTER_UNKNOWN_LABEL; | |
467 } | |
468 break; | |
469 | |
470 case AFTER_MAX_AGE_LABEL: | |
471 if (IsAsciiWhitespace(*tokenizer.token_begin())) | |
472 continue; | |
473 if (*tokenizer.token_begin() != '=') | |
474 return false; | |
475 DCHECK_EQ(tokenizer.token().length(), 1U); | |
476 state = AFTER_MAX_AGE_EQUALS; | |
477 break; | |
478 | |
479 case AFTER_MAX_AGE_EQUALS: | |
480 if (IsAsciiWhitespace(*tokenizer.token_begin())) | |
481 continue; | |
482 unquoted = HttpUtil::Unquote(tokenizer.token()); | |
483 if (!MaxAgeToInt(unquoted.begin(), | |
484 unquoted.end(), | |
485 &max_age_candidate)) | |
486 return false; | |
487 state = AFTER_MAX_AGE; | |
488 break; | |
489 | |
490 case AFTER_MAX_AGE: | |
491 case AFTER_INCLUDE_SUBDOMAINS: | |
492 if (IsAsciiWhitespace(*tokenizer.token_begin())) | |
493 continue; | |
494 else if (*tokenizer.token_begin() == ';') | |
495 state = DIRECTIVE_END; | |
496 else | |
497 return false; | |
498 break; | |
499 | |
500 case AFTER_UNKNOWN_LABEL: | |
501 // Consume and ignore the post-label contents (if any). | |
502 if (*tokenizer.token_begin() != ';') | |
503 continue; | |
504 state = DIRECTIVE_END; | |
505 break; | |
506 } | |
507 } | |
508 | |
509 // We've consumed all the input. Let's see what state we ended up in. | |
510 if (max_age_observed != 1 || | |
511 (include_subdomains_observed != 0 && include_subdomains_observed != 1)) { | |
512 return false; | |
513 } | |
514 | |
515 switch (state) { | |
516 case AFTER_MAX_AGE: | |
517 case AFTER_INCLUDE_SUBDOMAINS: | |
518 case AFTER_UNKNOWN_LABEL: | |
519 if (max_age_candidate > 0) { | |
520 upgrade_expiry = now + base::TimeDelta::FromSeconds(max_age_candidate); | |
521 upgrade_mode = MODE_FORCE_HTTPS; | |
522 } else { | |
523 upgrade_expiry = now; | |
524 upgrade_mode = MODE_DEFAULT; | |
525 } | |
526 include_subdomains = include_subdomains_candidate; | |
527 return true; | |
528 case START: | |
529 case DIRECTIVE_END: | |
530 case AFTER_MAX_AGE_LABEL: | |
531 case AFTER_MAX_AGE_EQUALS: | |
532 return false; | |
533 default: | |
534 NOTREACHED(); | |
535 return false; | |
536 } | |
537 } | |
538 | |
539 static bool AddHash(const std::string& type_and_base64, | |
540 HashValueVector* out) { | |
541 HashValue hash; | |
542 | |
543 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | |
544 return false; | |
545 | |
546 out->push_back(hash); | |
547 return true; | |
548 } | |
549 | |
550 TransportSecurityState::~TransportSecurityState() {} | 218 TransportSecurityState::~TransportSecurityState() {} |
551 | 219 |
552 void TransportSecurityState::DirtyNotify() { | 220 void TransportSecurityState::DirtyNotify() { |
553 DCHECK(CalledOnValidThread()); | 221 DCHECK(CalledOnValidThread()); |
554 | 222 |
555 if (delegate_) | 223 if (delegate_) |
556 delegate_->StateIsDirty(this); | 224 delegate_->StateIsDirty(this); |
557 } | 225 } |
558 | 226 |
559 // static | 227 // static |
(...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
883 memcmp(entries[j].dns_name, &canonicalized_host[i], | 551 memcmp(entries[j].dns_name, &canonicalized_host[i], |
884 entries[j].length) == 0) { | 552 entries[j].length) == 0) { |
885 if (!entries[j].include_subdomains && i != 0) { | 553 if (!entries[j].include_subdomains && i != 0) { |
886 *ret = false; | 554 *ret = false; |
887 } else { | 555 } else { |
888 out->include_subdomains = entries[j].include_subdomains; | 556 out->include_subdomains = entries[j].include_subdomains; |
889 *ret = true; | 557 *ret = true; |
890 if (!entries[j].https_required) | 558 if (!entries[j].https_required) |
891 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; | 559 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; |
892 if (entries[j].pins.required_hashes) { | 560 if (entries[j].pins.required_hashes) { |
893 const char* const* hash = entries[j].pins.required_hashes; | 561 const char* const* sha1_hash = entries[j].pins.required_hashes; |
894 while (*hash) { | 562 while (*sha1_hash) { |
895 bool ok = AddHash(*hash, &out->static_spki_hashes); | 563 AddHash(*sha1_hash, &out->static_spki_hashes); |
896 DCHECK(ok) << " failed to parse " << *hash; | 564 sha1_hash++; |
897 hash++; | |
898 } | 565 } |
899 } | 566 } |
900 if (entries[j].pins.excluded_hashes) { | 567 if (entries[j].pins.excluded_hashes) { |
901 const char* const* hash = entries[j].pins.excluded_hashes; | 568 const char* const* sha1_hash = entries[j].pins.excluded_hashes; |
902 while (*hash) { | 569 while (*sha1_hash) { |
903 bool ok = AddHash(*hash, &out->bad_static_spki_hashes); | 570 AddHash(*sha1_hash, &out->bad_static_spki_hashes); |
904 DCHECK(ok) << " failed to parse " << *hash; | 571 sha1_hash++; |
905 hash++; | |
906 } | 572 } |
907 } | 573 } |
908 } | 574 } |
909 return true; | 575 return true; |
910 } | 576 } |
911 } | 577 } |
912 return false; | 578 return false; |
913 } | 579 } |
914 | 580 |
915 #include "net/base/transport_security_state_static.h" | 581 #include "net/base/transport_security_state_static.h" |
(...skipping 18 matching lines...) Expand all Loading... |
934 if (entry->length == canonicalized_host.size() - i && | 600 if (entry->length == canonicalized_host.size() - i && |
935 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) { | 601 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) { |
936 return entry; | 602 return entry; |
937 } | 603 } |
938 } | 604 } |
939 } | 605 } |
940 | 606 |
941 return NULL; | 607 return NULL; |
942 } | 608 } |
943 | 609 |
| 610 bool TransportSecurityState::AddHSTSHeader(const std::string& host, |
| 611 const std::string& value) { |
| 612 base::Time now = base::Time::Now(); |
| 613 TransportSecurityState::DomainState domain_state; |
| 614 if (ParseHSTSHeader(now, value, &domain_state.upgrade_expiry, |
| 615 &domain_state.include_subdomains)) { |
| 616 // Handle max-age == 0 |
| 617 if (now == domain_state.upgrade_expiry) |
| 618 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; |
| 619 else |
| 620 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
| 621 domain_state.created = now; |
| 622 EnableHost(host, domain_state); |
| 623 return true; |
| 624 } |
| 625 return false; |
| 626 } |
| 627 |
| 628 bool TransportSecurityState::AddHPKPHeader(const std::string& host, |
| 629 const std::string& value, |
| 630 const SSLInfo& ssl_info) { |
| 631 base::Time now = base::Time::Now(); |
| 632 TransportSecurityState::DomainState domain_state; |
| 633 if (ParseHPKPHeader(now, value, ssl_info.public_key_hashes, |
| 634 &domain_state.dynamic_spki_hashes_expiry, |
| 635 &domain_state.dynamic_spki_hashes)) { |
| 636 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; |
| 637 domain_state.created = now; |
| 638 EnableHost(host, domain_state); |
| 639 return true; |
| 640 } |
| 641 return false; |
| 642 } |
| 643 |
944 // static | 644 // static |
945 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | 645 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, |
946 bool sni_enabled) { | 646 bool sni_enabled) { |
947 std::string canonicalized_host = CanonicalizeHost(host); | 647 std::string canonicalized_host = CanonicalizeHost(host); |
948 const struct HSTSPreload* entry = | 648 const struct HSTSPreload* entry = |
949 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 649 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
950 | 650 |
951 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 651 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
952 return true; | 652 return true; |
953 | 653 |
(...skipping 26 matching lines...) Expand all Loading... |
980 | 680 |
981 DCHECK(entry); | 681 DCHECK(entry); |
982 DCHECK(entry->pins.required_hashes); | 682 DCHECK(entry->pins.required_hashes); |
983 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | 683 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); |
984 | 684 |
985 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | 685 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
986 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | 686 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); |
987 } | 687 } |
988 | 688 |
989 // static | 689 // static |
990 const char* TransportSecurityState::HashValueLabel( | |
991 const HashValue& hash_value) { | |
992 switch (hash_value.tag) { | |
993 case HASH_VALUE_SHA1: | |
994 return "sha1/"; | |
995 case HASH_VALUE_SHA256: | |
996 return "sha256/"; | |
997 default: | |
998 NOTREACHED(); | |
999 LOG(WARNING) << "Invalid fingerprint of unknown type " << hash_value.tag; | |
1000 return "unknown/"; | |
1001 } | |
1002 } | |
1003 | |
1004 // static | |
1005 bool TransportSecurityState::IsBuildTimely() { | 690 bool TransportSecurityState::IsBuildTimely() { |
1006 const base::Time build_time = base::GetBuildTime(); | 691 const base::Time build_time = base::GetBuildTime(); |
1007 // We consider built-in information to be timely for 10 weeks. | 692 // We consider built-in information to be timely for 10 weeks. |
1008 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; | 693 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; |
1009 } | 694 } |
1010 | 695 |
1011 bool TransportSecurityState::GetStaticDomainState( | 696 bool TransportSecurityState::GetStaticDomainState( |
1012 const std::string& canonicalized_host, | 697 const std::string& canonicalized_host, |
1013 bool sni_enabled, | 698 bool sni_enabled, |
1014 DomainState* out) { | 699 DomainState* out) { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1049 void TransportSecurityState::AddOrUpdateEnabledHosts( | 734 void TransportSecurityState::AddOrUpdateEnabledHosts( |
1050 const std::string& hashed_host, const DomainState& state) { | 735 const std::string& hashed_host, const DomainState& state) { |
1051 enabled_hosts_[hashed_host] = state; | 736 enabled_hosts_[hashed_host] = state; |
1052 } | 737 } |
1053 | 738 |
1054 void TransportSecurityState::AddOrUpdateForcedHosts( | 739 void TransportSecurityState::AddOrUpdateForcedHosts( |
1055 const std::string& hashed_host, const DomainState& state) { | 740 const std::string& hashed_host, const DomainState& state) { |
1056 forced_hosts_[hashed_host] = state; | 741 forced_hosts_[hashed_host] = state; |
1057 } | 742 } |
1058 | 743 |
1059 static std::string HashesToBase64String( | |
1060 const HashValueVector& hashes) { | |
1061 std::vector<std::string> hashes_strs; | |
1062 for (HashValueVector::const_iterator | |
1063 i = hashes.begin(); i != hashes.end(); i++) { | |
1064 std::string s; | |
1065 const std::string hash_str(reinterpret_cast<const char*>(i->data()), | |
1066 i->size()); | |
1067 base::Base64Encode(hash_str, &s); | |
1068 hashes_strs.push_back(s); | |
1069 } | |
1070 | |
1071 return JoinString(hashes_strs, ','); | |
1072 } | |
1073 | |
1074 TransportSecurityState::DomainState::DomainState() | 744 TransportSecurityState::DomainState::DomainState() |
1075 : upgrade_mode(MODE_FORCE_HTTPS), | 745 : upgrade_mode(MODE_FORCE_HTTPS), |
1076 created(base::Time::Now()), | 746 created(base::Time::Now()), |
1077 include_subdomains(false) { | 747 include_subdomains(false) { |
1078 } | 748 } |
1079 | 749 |
1080 TransportSecurityState::DomainState::~DomainState() { | 750 TransportSecurityState::DomainState::~DomainState() { |
1081 } | 751 } |
1082 | 752 |
1083 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( | 753 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1126 return true; | 796 return true; |
1127 } | 797 } |
1128 | 798 |
1129 bool TransportSecurityState::DomainState::HasPins() const { | 799 bool TransportSecurityState::DomainState::HasPins() const { |
1130 return static_spki_hashes.size() > 0 || | 800 return static_spki_hashes.size() > 0 || |
1131 bad_static_spki_hashes.size() > 0 || | 801 bad_static_spki_hashes.size() > 0 || |
1132 dynamic_spki_hashes.size() > 0; | 802 dynamic_spki_hashes.size() > 0; |
1133 } | 803 } |
1134 | 804 |
1135 } // namespace | 805 } // namespace |
OLD | NEW |