| 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 |