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/logging.h" | 21 #include "base/logging.h" |
22 #include "base/memory/scoped_ptr.h" | 22 #include "base/memory/scoped_ptr.h" |
23 #include "base/metrics/histogram.h" | 23 #include "base/metrics/histogram.h" |
24 #include "base/sha1.h" | 24 #include "base/sha1.h" |
25 #include "base/string_number_conversions.h" | 25 #include "base/string_number_conversions.h" |
26 #include "base/string_tokenizer.h" | |
27 #include "base/string_util.h" | 26 #include "base/string_util.h" |
28 #include "base/time.h" | 27 #include "base/time.h" |
29 #include "base/utf_string_conversions.h" | 28 #include "base/utf_string_conversions.h" |
30 #include "base/values.h" | 29 #include "base/values.h" |
31 #include "crypto/sha2.h" | 30 #include "crypto/sha2.h" |
32 #include "googleurl/src/gurl.h" | 31 #include "googleurl/src/gurl.h" |
33 #include "net/base/dns_util.h" | 32 #include "net/base/dns_util.h" |
34 #include "net/base/ssl_info.h" | 33 #include "net/base/ssl_info.h" |
35 #include "net/base/x509_cert_types.h" | 34 #include "net/base/x509_cert_types.h" |
36 #include "net/base/x509_certificate.h" | 35 #include "net/base/x509_certificate.h" |
37 #include "net/http/http_util.h" | 36 #include "net/http/http_security_headers.h" |
38 | 37 |
39 #if defined(USE_OPENSSL) | 38 #if defined(USE_OPENSSL) |
40 #include "crypto/openssl_util.h" | 39 #include "crypto/openssl_util.h" |
41 #endif | 40 #endif |
42 | 41 |
43 namespace net { | 42 namespace net { |
44 | 43 |
45 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year | |
46 | |
47 static std::string HashHost(const std::string& canonicalized_host) { | 44 static std::string HashHost(const std::string& canonicalized_host) { |
48 char hashed[crypto::kSHA256Length]; | 45 char hashed[crypto::kSHA256Length]; |
49 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | 46 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); |
50 return std::string(hashed, sizeof(hashed)); | 47 return std::string(hashed, sizeof(hashed)); |
51 } | 48 } |
52 | 49 |
53 TransportSecurityState::TransportSecurityState() | 50 TransportSecurityState::TransportSecurityState() |
54 : delegate_(NULL) { | 51 : delegate_(NULL) { |
55 } | 52 } |
56 | 53 |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
175 enabled_hosts_.erase(i++); | 172 enabled_hosts_.erase(i++); |
176 } else { | 173 } else { |
177 i++; | 174 i++; |
178 } | 175 } |
179 } | 176 } |
180 | 177 |
181 if (dirtied) | 178 if (dirtied) |
182 DirtyNotify(); | 179 DirtyNotify(); |
183 } | 180 } |
184 | 181 |
185 // MaxAgeToInt converts a string representation of a number of seconds into a | |
186 // int. We use strtol in order to handle overflow correctly. The string may | |
187 // contain an arbitary number which we should truncate correctly rather than | |
188 // throwing a parse failure. | |
189 static bool MaxAgeToInt(std::string::const_iterator begin, | |
190 std::string::const_iterator end, | |
191 int* result) { | |
192 const std::string s(begin, end); | |
193 char* endptr; | |
194 long int i = strtol(s.data(), &endptr, 10 /* base */); | |
195 if (*endptr || i < 0) | |
196 return false; | |
197 if (i > TransportSecurityState::kMaxHSTSAgeSecs) | |
198 i = TransportSecurityState::kMaxHSTSAgeSecs; | |
199 *result = i; | |
200 return true; | |
201 } | |
202 | |
203 // Strip, Split, StringPair, and ParsePins are private implementation details | |
204 // of ParsePinsHeader(std::string&, DomainState&). | |
205 static std::string Strip(const std::string& source) { | |
206 if (source.empty()) | |
207 return source; | |
208 | |
209 std::string::const_iterator start = source.begin(); | |
210 std::string::const_iterator end = source.end(); | |
211 HttpUtil::TrimLWS(&start, &end); | |
212 return std::string(start, end); | |
213 } | |
214 | |
215 typedef std::pair<std::string, std::string> StringPair; | |
216 | |
217 static StringPair Split(const std::string& source, char delimiter) { | |
218 StringPair pair; | |
219 size_t point = source.find(delimiter); | |
220 | |
221 pair.first = source.substr(0, point); | |
222 if (std::string::npos != point) | |
223 pair.second = source.substr(point + 1); | |
224 | |
225 return pair; | |
226 } | |
227 | |
228 // static | |
229 bool TransportSecurityState::ParsePin(const std::string& value, | |
230 HashValue* out) { | |
231 StringPair slash = Split(Strip(value), '/'); | |
232 | |
233 if (slash.first == "sha1") | |
234 out->tag = HASH_VALUE_SHA1; | |
235 else if (slash.first == "sha256") | |
236 out->tag = HASH_VALUE_SHA256; | |
237 else | |
238 return false; | |
239 | |
240 std::string decoded; | |
241 if (!base::Base64Decode(slash.second, &decoded) || | |
242 decoded.size() != out->size()) { | |
243 return false; | |
244 } | |
245 | |
246 memcpy(out->data(), decoded.data(), out->size()); | |
247 return true; | |
248 } | |
249 | |
250 static bool ParseAndAppendPin(const std::string& value, | |
251 HashValueTag tag, | |
252 HashValueVector* hashes) { | |
253 std::string unquoted = HttpUtil::Unquote(value); | |
254 std::string decoded; | |
255 | |
256 // This code has to assume that 32 bytes is SHA-256 and 20 bytes is SHA-1. | |
257 // Currently, those are the only two possibilities, so the assumption is | |
258 // valid. | |
259 if (!base::Base64Decode(unquoted, &decoded)) | |
260 return false; | |
261 | |
262 HashValue hash(tag); | |
263 if (decoded.size() != hash.size()) | |
264 return false; | |
265 | |
266 memcpy(hash.data(), decoded.data(), hash.size()); | |
267 hashes->push_back(hash); | |
268 return true; | |
269 } | |
270 | |
271 struct HashValuesEqualPredicate { | |
272 explicit HashValuesEqualPredicate(const HashValue& fingerprint) : | |
273 fingerprint_(fingerprint) {} | |
274 | |
275 bool operator()(const HashValue& other) const { | |
276 return fingerprint_.Equals(other); | |
277 } | |
278 | |
279 const HashValue& fingerprint_; | |
280 }; | |
281 | |
282 // Returns true iff there is an item in |pins| which is not present in | |
283 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". | |
284 static bool IsBackupPinPresent(const HashValueVector& pins, | |
285 const HashValueVector& from_cert_chain) { | |
286 for (HashValueVector::const_iterator | |
287 i = pins.begin(); i != pins.end(); ++i) { | |
288 HashValueVector::const_iterator j = | |
289 std::find_if(from_cert_chain.begin(), from_cert_chain.end(), | |
290 HashValuesEqualPredicate(*i)); | |
291 if (j == from_cert_chain.end()) | |
292 return true; | |
293 } | |
294 | |
295 return false; | |
296 } | |
297 | |
298 // Returns true if the intersection of |a| and |b| is not empty. If either | 182 // Returns true if the intersection of |a| and |b| is not empty. If either |
299 // |a| or |b| is empty, returns false. | 183 // |a| or |b| is empty, returns false. |
300 static bool HashesIntersect(const HashValueVector& a, | 184 static bool HashesIntersect(const HashValueVector& a, |
301 const HashValueVector& b) { | 185 const HashValueVector& b) { |
palmer
2012/12/06 21:20:17
NIT: Whitespace
unsafe
2012/12/07 09:58:26
Done.
| |
302 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { | 186 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { |
303 HashValueVector::const_iterator j = | 187 HashValueVector::const_iterator j = |
304 std::find_if(b.begin(), b.end(), HashValuesEqualPredicate(*i)); | 188 std::find_if(b.begin(), b.end(), HashValuesEqual(*i)); |
305 if (j != b.end()) | 189 if (j != b.end()) |
306 return true; | 190 return true; |
307 } | 191 } |
308 | |
309 return false; | 192 return false; |
310 } | 193 } |
311 | 194 |
312 // Returns true iff |pins| contains both a live and a backup pin. A live pin | 195 static bool AddHash(const char* sha1_hash, |
313 // is a pin whose SPKI is present in the certificate chain in |ssl_info|. A | |
314 // backup pin is a pin intended for disaster recovery, not day-to-day use, and | |
315 // thus must be absent from the certificate chain. The Public-Key-Pins header | |
316 // specification requires both. | |
317 static bool IsPinListValid(const HashValueVector& pins, | |
318 const SSLInfo& ssl_info) { | |
319 // Fast fail: 1 live + 1 backup = at least 2 pins. (Check for actual | |
320 // liveness and backupness below.) | |
321 if (pins.size() < 2) | |
322 return false; | |
323 | |
324 const HashValueVector& from_cert_chain = ssl_info.public_key_hashes; | |
325 if (from_cert_chain.empty()) | |
326 return false; | |
327 | |
328 return IsBackupPinPresent(pins, from_cert_chain) && | |
329 HashesIntersect(pins, from_cert_chain); | |
330 } | |
331 | |
332 // "Public-Key-Pins" ":" | |
333 // "max-age" "=" delta-seconds ";" | |
334 // "pin-" algo "=" base64 [ ";" ... ] | |
335 bool TransportSecurityState::DomainState::ParsePinsHeader( | |
336 const base::Time& now, | |
337 const std::string& value, | |
338 const SSLInfo& ssl_info) { | |
339 bool parsed_max_age = false; | |
340 int max_age_candidate = 0; | |
341 HashValueVector pins; | |
342 | |
343 std::string source = value; | |
344 | |
345 while (!source.empty()) { | |
346 StringPair semicolon = Split(source, ';'); | |
347 semicolon.first = Strip(semicolon.first); | |
348 semicolon.second = Strip(semicolon.second); | |
349 StringPair equals = Split(semicolon.first, '='); | |
350 equals.first = Strip(equals.first); | |
351 equals.second = Strip(equals.second); | |
352 | |
353 if (LowerCaseEqualsASCII(equals.first, "max-age")) { | |
354 if (equals.second.empty() || | |
355 !MaxAgeToInt(equals.second.begin(), equals.second.end(), | |
356 &max_age_candidate)) { | |
357 return false; | |
358 } | |
359 if (max_age_candidate > kMaxHSTSAgeSecs) | |
360 max_age_candidate = kMaxHSTSAgeSecs; | |
361 parsed_max_age = true; | |
362 } else if (StartsWithASCII(equals.first, "pin-", false)) { | |
363 HashValueTag tag; | |
364 if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { | |
365 tag = HASH_VALUE_SHA1; | |
366 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | |
367 tag = HASH_VALUE_SHA256; | |
368 } else { | |
369 LOG(WARNING) << "Ignoring pin of unknown type: " << equals.first; | |
370 return false; | |
371 } | |
372 if (!ParseAndAppendPin(equals.second, tag, &pins)) | |
373 return false; | |
374 } else { | |
375 // Silently ignore unknown directives for forward compatibility. | |
376 } | |
377 | |
378 source = semicolon.second; | |
379 } | |
380 | |
381 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) | |
382 return false; | |
383 | |
384 dynamic_spki_hashes_expiry = | |
385 now + base::TimeDelta::FromSeconds(max_age_candidate); | |
386 | |
387 dynamic_spki_hashes.clear(); | |
388 if (max_age_candidate > 0) { | |
389 for (HashValueVector::const_iterator i = pins.begin(); | |
390 i != pins.end(); ++i) { | |
391 dynamic_spki_hashes.push_back(*i); | |
392 } | |
393 } | |
394 | |
395 return true; | |
396 } | |
397 | |
398 // Parse the Strict-Transport-Security header, as currently defined in | |
399 // http://tools.ietf.org/html/draft-ietf-websec-strict-transport-sec-14: | |
400 // | |
401 // Strict-Transport-Security = "Strict-Transport-Security" ":" | |
402 // [ directive ] *( ";" [ directive ] ) | |
403 // | |
404 // directive = directive-name [ "=" directive-value ] | |
405 // directive-name = token | |
406 // directive-value = token | quoted-string | |
407 // | |
408 // 1. The order of appearance of directives is not significant. | |
409 // | |
410 // 2. All directives MUST appear only once in an STS header field. | |
411 // Directives are either optional or required, as stipulated in | |
412 // their definitions. | |
413 // | |
414 // 3. Directive names are case-insensitive. | |
415 // | |
416 // 4. UAs MUST ignore any STS header fields containing directives, or | |
417 // other header field value data, that does not conform to the | |
418 // syntax defined in this specification. | |
419 // | |
420 // 5. If an STS header field contains directive(s) not recognized by | |
421 // the UA, the UA MUST ignore the unrecognized directives and if the | |
422 // STS header field otherwise satisfies the above requirements (1 | |
423 // through 4), the UA MUST process the recognized directives. | |
424 bool TransportSecurityState::DomainState::ParseSTSHeader( | |
425 const base::Time& now, | |
426 const std::string& value) { | |
427 int max_age_candidate = 0; | |
428 bool include_subdomains_candidate = false; | |
429 | |
430 // We must see max-age exactly once. | |
431 int max_age_observed = 0; | |
432 // We must see includeSubdomains exactly 0 or 1 times. | |
433 int include_subdomains_observed = 0; | |
434 | |
435 enum ParserState { | |
436 START, | |
437 AFTER_MAX_AGE_LABEL, | |
438 AFTER_MAX_AGE_EQUALS, | |
439 AFTER_MAX_AGE, | |
440 AFTER_INCLUDE_SUBDOMAINS, | |
441 AFTER_UNKNOWN_LABEL, | |
442 DIRECTIVE_END | |
443 } state = START; | |
444 | |
445 StringTokenizer tokenizer(value, " \t=;"); | |
446 tokenizer.set_options(StringTokenizer::RETURN_DELIMS); | |
447 tokenizer.set_quote_chars("\""); | |
448 std::string unquoted; | |
449 while (tokenizer.GetNext()) { | |
450 DCHECK(!tokenizer.token_is_delim() || tokenizer.token().length() == 1); | |
451 switch (state) { | |
452 case START: | |
453 case DIRECTIVE_END: | |
454 if (IsAsciiWhitespace(*tokenizer.token_begin())) | |
455 continue; | |
456 if (LowerCaseEqualsASCII(tokenizer.token(), "max-age")) { | |
457 state = AFTER_MAX_AGE_LABEL; | |
458 max_age_observed++; | |
459 } else if (LowerCaseEqualsASCII(tokenizer.token(), | |
460 "includesubdomains")) { | |
461 state = AFTER_INCLUDE_SUBDOMAINS; | |
462 include_subdomains_observed++; | |
463 include_subdomains_candidate = true; | |
464 } else { | |
465 state = AFTER_UNKNOWN_LABEL; | |
466 } | |
467 break; | |
468 | |
469 case AFTER_MAX_AGE_LABEL: | |
470 if (IsAsciiWhitespace(*tokenizer.token_begin())) | |
471 continue; | |
472 if (*tokenizer.token_begin() != '=') | |
473 return false; | |
474 DCHECK_EQ(tokenizer.token().length(), 1U); | |
475 state = AFTER_MAX_AGE_EQUALS; | |
476 break; | |
477 | |
478 case AFTER_MAX_AGE_EQUALS: | |
479 if (IsAsciiWhitespace(*tokenizer.token_begin())) | |
480 continue; | |
481 unquoted = HttpUtil::Unquote(tokenizer.token()); | |
482 if (!MaxAgeToInt(unquoted.begin(), | |
483 unquoted.end(), | |
484 &max_age_candidate)) | |
485 return false; | |
486 state = AFTER_MAX_AGE; | |
487 break; | |
488 | |
489 case AFTER_MAX_AGE: | |
490 case AFTER_INCLUDE_SUBDOMAINS: | |
491 if (IsAsciiWhitespace(*tokenizer.token_begin())) | |
492 continue; | |
493 else if (*tokenizer.token_begin() == ';') | |
494 state = DIRECTIVE_END; | |
495 else | |
496 return false; | |
497 break; | |
498 | |
499 case AFTER_UNKNOWN_LABEL: | |
500 // Consume and ignore the post-label contents (if any). | |
501 if (*tokenizer.token_begin() != ';') | |
502 continue; | |
503 state = DIRECTIVE_END; | |
504 break; | |
505 } | |
506 } | |
507 | |
508 // We've consumed all the input. Let's see what state we ended up in. | |
509 if (max_age_observed != 1 || | |
510 (include_subdomains_observed != 0 && include_subdomains_observed != 1)) { | |
511 return false; | |
512 } | |
513 | |
514 switch (state) { | |
515 case AFTER_MAX_AGE: | |
516 case AFTER_INCLUDE_SUBDOMAINS: | |
517 case AFTER_UNKNOWN_LABEL: | |
518 if (max_age_candidate > 0) { | |
519 upgrade_expiry = now + base::TimeDelta::FromSeconds(max_age_candidate); | |
520 upgrade_mode = MODE_FORCE_HTTPS; | |
521 } else { | |
522 upgrade_expiry = now; | |
523 upgrade_mode = MODE_DEFAULT; | |
524 } | |
525 include_subdomains = include_subdomains_candidate; | |
526 return true; | |
527 case START: | |
528 case DIRECTIVE_END: | |
529 case AFTER_MAX_AGE_LABEL: | |
530 case AFTER_MAX_AGE_EQUALS: | |
531 return false; | |
532 default: | |
533 NOTREACHED(); | |
534 return false; | |
535 } | |
536 } | |
537 | |
538 static bool AddHash(const std::string& type_and_base64, | |
539 HashValueVector* out) { | 196 HashValueVector* out) { |
540 HashValue hash; | 197 HashValue hash(HASH_VALUE_SHA1); |
541 | 198 memcpy(hash.data(), sha1_hash, 20); |
542 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | |
543 return false; | |
544 | |
545 out->push_back(hash); | 199 out->push_back(hash); |
546 return true; | 200 return true; |
547 } | 201 } |
548 | 202 |
549 TransportSecurityState::~TransportSecurityState() {} | 203 TransportSecurityState::~TransportSecurityState() {} |
550 | 204 |
551 void TransportSecurityState::DirtyNotify() { | 205 void TransportSecurityState::DirtyNotify() { |
552 DCHECK(CalledOnValidThread()); | 206 DCHECK(CalledOnValidThread()); |
553 | 207 |
554 if (delegate_) | 208 if (delegate_) |
(...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
878 memcmp(entries[j].dns_name, &canonicalized_host[i], | 532 memcmp(entries[j].dns_name, &canonicalized_host[i], |
879 entries[j].length) == 0) { | 533 entries[j].length) == 0) { |
880 if (!entries[j].include_subdomains && i != 0) { | 534 if (!entries[j].include_subdomains && i != 0) { |
881 *ret = false; | 535 *ret = false; |
882 } else { | 536 } else { |
883 out->include_subdomains = entries[j].include_subdomains; | 537 out->include_subdomains = entries[j].include_subdomains; |
884 *ret = true; | 538 *ret = true; |
885 if (!entries[j].https_required) | 539 if (!entries[j].https_required) |
886 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; | 540 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; |
887 if (entries[j].pins.required_hashes) { | 541 if (entries[j].pins.required_hashes) { |
888 const char* const* hash = entries[j].pins.required_hashes; | 542 const char* const* sha1_hash = entries[j].pins.required_hashes; |
889 while (*hash) { | 543 while (*sha1_hash) { |
890 bool ok = AddHash(*hash, &out->static_spki_hashes); | 544 AddHash(*sha1_hash, &out->static_spki_hashes); |
891 DCHECK(ok) << " failed to parse " << *hash; | 545 sha1_hash++; |
892 hash++; | |
893 } | 546 } |
894 } | 547 } |
895 if (entries[j].pins.excluded_hashes) { | 548 if (entries[j].pins.excluded_hashes) { |
896 const char* const* hash = entries[j].pins.excluded_hashes; | 549 const char* const* sha1_hash = entries[j].pins.excluded_hashes; |
897 while (*hash) { | 550 while (*sha1_hash) { |
898 bool ok = AddHash(*hash, &out->bad_static_spki_hashes); | 551 AddHash(*sha1_hash, &out->bad_static_spki_hashes); |
899 DCHECK(ok) << " failed to parse " << *hash; | 552 sha1_hash++; |
900 hash++; | |
901 } | 553 } |
902 } | 554 } |
903 } | 555 } |
904 return true; | 556 return true; |
905 } | 557 } |
906 } | 558 } |
907 return false; | 559 return false; |
908 } | 560 } |
909 | 561 |
910 #include "net/base/transport_security_state_static.h" | 562 #include "net/base/transport_security_state_static.h" |
(...skipping 18 matching lines...) Expand all Loading... | |
929 if (entry->length == canonicalized_host.size() - i && | 581 if (entry->length == canonicalized_host.size() - i && |
930 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) { | 582 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) { |
931 return entry; | 583 return entry; |
932 } | 584 } |
933 } | 585 } |
934 } | 586 } |
935 | 587 |
936 return NULL; | 588 return NULL; |
937 } | 589 } |
938 | 590 |
591 bool TransportSecurityState::AddHSTSHeader(const std::string& host, | |
592 const std::string& value) { | |
593 base::Time now = base::Time::Now(); | |
594 TransportSecurityState::DomainState domain_state; | |
595 if (ParseHSTSHeader(now, value, | |
596 &domain_state.upgrade_expiry, | |
597 &domain_state.include_subdomains)) { | |
598 // Handle max-age == 0 | |
599 if (now == domain_state.upgrade_expiry) | |
600 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | |
601 else | |
602 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; | |
603 domain_state.created = now; | |
604 EnableHost(host, domain_state); | |
605 return true; | |
606 } | |
607 return false; | |
608 } | |
609 | |
610 bool TransportSecurityState::AddHPKPHeader(const std::string& host, | |
611 const std::string& value, | |
palmer
2012/12/06 21:20:17
NIT: Whitespace
unsafe
2012/12/07 09:58:26
Done.
| |
612 const SSLInfo& ssl_info) { | |
613 base::Time now = base::Time::Now(); | |
614 TransportSecurityState::DomainState domain_state; | |
615 if (ParseHPKPHeader(now, value, | |
616 ssl_info.public_key_hashes, | |
617 &domain_state.dynamic_spki_hashes_expiry, | |
618 &domain_state.dynamic_spki_hashes)) { | |
619 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | |
620 domain_state.created = now; | |
621 EnableHost(host, domain_state); | |
622 return true; | |
623 } | |
624 return false; | |
625 } | |
626 | |
939 // static | 627 // static |
940 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | 628 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, |
941 bool sni_enabled) { | 629 bool sni_enabled) { |
942 std::string canonicalized_host = CanonicalizeHost(host); | 630 std::string canonicalized_host = CanonicalizeHost(host); |
943 const struct HSTSPreload* entry = | 631 const struct HSTSPreload* entry = |
944 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 632 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
945 | 633 |
946 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 634 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
947 return true; | 635 return true; |
948 | 636 |
(...skipping 25 matching lines...) Expand all Loading... | |
974 } | 662 } |
975 | 663 |
976 DCHECK(entry); | 664 DCHECK(entry); |
977 DCHECK(entry->pins.required_hashes); | 665 DCHECK(entry->pins.required_hashes); |
978 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | 666 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); |
979 | 667 |
980 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | 668 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
981 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | 669 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); |
982 } | 670 } |
983 | 671 |
984 // static | |
985 const char* TransportSecurityState::HashValueLabel( | |
986 const HashValue& hash_value) { | |
987 switch (hash_value.tag) { | |
988 case HASH_VALUE_SHA1: | |
989 return "sha1/"; | |
990 case HASH_VALUE_SHA256: | |
991 return "sha256/"; | |
992 default: | |
993 NOTREACHED(); | |
994 LOG(WARNING) << "Invalid fingerprint of unknown type " << hash_value.tag; | |
995 return "unknown/"; | |
996 } | |
997 } | |
998 | |
999 bool TransportSecurityState::GetStaticDomainState( | 672 bool TransportSecurityState::GetStaticDomainState( |
1000 const std::string& canonicalized_host, | 673 const std::string& canonicalized_host, |
1001 bool sni_enabled, | 674 bool sni_enabled, |
1002 DomainState* out) { | 675 DomainState* out) { |
1003 DCHECK(CalledOnValidThread()); | 676 DCHECK(CalledOnValidThread()); |
1004 | 677 |
1005 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; | 678 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
1006 out->include_subdomains = false; | 679 out->include_subdomains = false; |
1007 | 680 |
1008 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 681 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
(...skipping 24 matching lines...) Expand all Loading... | |
1033 void TransportSecurityState::AddOrUpdateEnabledHosts( | 706 void TransportSecurityState::AddOrUpdateEnabledHosts( |
1034 const std::string& hashed_host, const DomainState& state) { | 707 const std::string& hashed_host, const DomainState& state) { |
1035 enabled_hosts_[hashed_host] = state; | 708 enabled_hosts_[hashed_host] = state; |
1036 } | 709 } |
1037 | 710 |
1038 void TransportSecurityState::AddOrUpdateForcedHosts( | 711 void TransportSecurityState::AddOrUpdateForcedHosts( |
1039 const std::string& hashed_host, const DomainState& state) { | 712 const std::string& hashed_host, const DomainState& state) { |
1040 forced_hosts_[hashed_host] = state; | 713 forced_hosts_[hashed_host] = state; |
1041 } | 714 } |
1042 | 715 |
1043 static std::string HashesToBase64String( | 716 static std::string HashesToBase64String(const HashValueVector& hashes) { |
palmer
2012/12/06 21:20:17
We've duplicated this in net_internals_ui.cc. Can
unsafe
2012/12/07 09:58:26
Moved this into an unnamed namespace. Will commen
| |
1044 const HashValueVector& hashes) { | 717 std::string str; |
1045 std::vector<std::string> hashes_strs; | 718 for (size_t i = 0; i != hashes.size(); ++i) { |
1046 for (HashValueVector::const_iterator | 719 if (i != 0) |
1047 i = hashes.begin(); i != hashes.end(); i++) { | 720 str += ","; |
1048 std::string s; | 721 str += hashes[i].ToString(); |
1049 const std::string hash_str(reinterpret_cast<const char*>(i->data()), | |
1050 i->size()); | |
1051 base::Base64Encode(hash_str, &s); | |
1052 hashes_strs.push_back(s); | |
1053 } | 722 } |
1054 | 723 return str; |
1055 return JoinString(hashes_strs, ','); | |
1056 } | 724 } |
1057 | 725 |
1058 TransportSecurityState::DomainState::DomainState() | 726 TransportSecurityState::DomainState::DomainState() |
1059 : upgrade_mode(MODE_FORCE_HTTPS), | 727 : upgrade_mode(MODE_FORCE_HTTPS), |
1060 created(base::Time::Now()), | 728 created(base::Time::Now()), |
1061 include_subdomains(false) { | 729 include_subdomains(false) { |
1062 } | 730 } |
1063 | 731 |
1064 TransportSecurityState::DomainState::~DomainState() { | 732 TransportSecurityState::DomainState::~DomainState() { |
1065 } | 733 } |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1110 return true; | 778 return true; |
1111 } | 779 } |
1112 | 780 |
1113 bool TransportSecurityState::DomainState::HasPins() const { | 781 bool TransportSecurityState::DomainState::HasPins() const { |
1114 return static_spki_hashes.size() > 0 || | 782 return static_spki_hashes.size() > 0 || |
1115 bad_static_spki_hashes.size() > 0 || | 783 bad_static_spki_hashes.size() > 0 || |
1116 dynamic_spki_hashes.size() > 0; | 784 dynamic_spki_hashes.size() > 0; |
1117 } | 785 } |
1118 | 786 |
1119 } // namespace | 787 } // namespace |
OLD | NEW |