Chromium Code Reviews| 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 | 44 namespace { |
| 46 | 45 |
| 47 static std::string HashHost(const std::string& canonicalized_host) { | 46 std::string HashesToBase64String(const HashValueVector& hashes) { |
| 47 std::string str; | |
| 48 for (size_t i = 0; i != hashes.size(); ++i) { | |
| 49 if (i != 0) | |
| 50 str += ","; | |
| 51 str += hashes[i].ToString(); | |
| 52 } | |
| 53 return str; | |
| 54 } | |
| 55 | |
| 56 std::string HashHost(const std::string& canonicalized_host) { | |
| 48 char hashed[crypto::kSHA256Length]; | 57 char hashed[crypto::kSHA256Length]; |
| 49 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | 58 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); |
| 50 return std::string(hashed, sizeof(hashed)); | 59 return std::string(hashed, sizeof(hashed)); |
| 51 } | 60 } |
| 52 | 61 |
| 62 // Returns true if the intersection of |a| and |b| is not empty. If either | |
| 63 // |a| or |b| is empty, returns false. | |
| 64 bool HashesIntersect(const HashValueVector& a, | |
| 65 const HashValueVector& b) { | |
| 66 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { | |
| 67 HashValueVector::const_iterator j = | |
| 68 std::find_if(b.begin(), b.end(), HashValuesEqual(*i)); | |
| 69 if (j != b.end()) | |
| 70 return true; | |
| 71 } | |
| 72 return false; | |
| 73 } | |
| 74 | |
| 75 bool AddHash(const char* sha1_hash, | |
| 76 HashValueVector* out) { | |
| 77 HashValue hash(HASH_VALUE_SHA1); | |
| 78 memcpy(hash.data(), sha1_hash, 20); | |
|
Ryan Sleevi
2012/12/07 23:37:21
nit: 20 -> hash.size()?
unsafe
2012/12/08 09:22:42
Done.
| |
| 79 out->push_back(hash); | |
| 80 return true; | |
| 81 } | |
| 82 | |
| 83 } // namespace | |
| 84 | |
| 53 TransportSecurityState::TransportSecurityState() | 85 TransportSecurityState::TransportSecurityState() |
| 54 : delegate_(NULL) { | 86 : delegate_(NULL) { |
| 55 } | 87 } |
| 56 | 88 |
| 57 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state) | 89 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state) |
| 58 : iterator_(state.enabled_hosts_.begin()), | 90 : iterator_(state.enabled_hosts_.begin()), |
| 59 end_(state.enabled_hosts_.end()) { | 91 end_(state.enabled_hosts_.end()) { |
| 60 } | 92 } |
| 61 | 93 |
| 62 TransportSecurityState::Iterator::~Iterator() {} | 94 TransportSecurityState::Iterator::~Iterator() {} |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 175 enabled_hosts_.erase(i++); | 207 enabled_hosts_.erase(i++); |
| 176 } else { | 208 } else { |
| 177 i++; | 209 i++; |
| 178 } | 210 } |
| 179 } | 211 } |
| 180 | 212 |
| 181 if (dirtied) | 213 if (dirtied) |
| 182 DirtyNotify(); | 214 DirtyNotify(); |
| 183 } | 215 } |
| 184 | 216 |
| 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 | |
| 299 // |a| or |b| is empty, returns false. | |
| 300 static bool HashesIntersect(const HashValueVector& a, | |
| 301 const HashValueVector& b) { | |
| 302 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { | |
| 303 HashValueVector::const_iterator j = | |
| 304 std::find_if(b.begin(), b.end(), HashValuesEqualPredicate(*i)); | |
| 305 if (j != b.end()) | |
| 306 return true; | |
| 307 } | |
| 308 | |
| 309 return false; | |
| 310 } | |
| 311 | |
| 312 // Returns true iff |pins| contains both a live and a backup pin. A live pin | |
| 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) { | |
| 540 HashValue hash; | |
| 541 | |
| 542 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | |
| 543 return false; | |
| 544 | |
| 545 out->push_back(hash); | |
| 546 return true; | |
| 547 } | |
| 548 | |
| 549 TransportSecurityState::~TransportSecurityState() {} | 217 TransportSecurityState::~TransportSecurityState() {} |
| 550 | 218 |
| 551 void TransportSecurityState::DirtyNotify() { | 219 void TransportSecurityState::DirtyNotify() { |
| 552 DCHECK(CalledOnValidThread()); | 220 DCHECK(CalledOnValidThread()); |
| 553 | 221 |
| 554 if (delegate_) | 222 if (delegate_) |
| 555 delegate_->StateIsDirty(this); | 223 delegate_->StateIsDirty(this); |
| 556 } | 224 } |
| 557 | 225 |
| 558 // static | 226 // static |
| (...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 878 memcmp(entries[j].dns_name, &canonicalized_host[i], | 546 memcmp(entries[j].dns_name, &canonicalized_host[i], |
| 879 entries[j].length) == 0) { | 547 entries[j].length) == 0) { |
| 880 if (!entries[j].include_subdomains && i != 0) { | 548 if (!entries[j].include_subdomains && i != 0) { |
| 881 *ret = false; | 549 *ret = false; |
| 882 } else { | 550 } else { |
| 883 out->include_subdomains = entries[j].include_subdomains; | 551 out->include_subdomains = entries[j].include_subdomains; |
| 884 *ret = true; | 552 *ret = true; |
| 885 if (!entries[j].https_required) | 553 if (!entries[j].https_required) |
| 886 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; | 554 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; |
| 887 if (entries[j].pins.required_hashes) { | 555 if (entries[j].pins.required_hashes) { |
| 888 const char* const* hash = entries[j].pins.required_hashes; | 556 const char* const* sha1_hash = entries[j].pins.required_hashes; |
| 889 while (*hash) { | 557 while (*sha1_hash) { |
| 890 bool ok = AddHash(*hash, &out->static_spki_hashes); | 558 AddHash(*sha1_hash, &out->static_spki_hashes); |
| 891 DCHECK(ok) << " failed to parse " << *hash; | 559 sha1_hash++; |
| 892 hash++; | |
| 893 } | 560 } |
| 894 } | 561 } |
| 895 if (entries[j].pins.excluded_hashes) { | 562 if (entries[j].pins.excluded_hashes) { |
| 896 const char* const* hash = entries[j].pins.excluded_hashes; | 563 const char* const* sha1_hash = entries[j].pins.excluded_hashes; |
| 897 while (*hash) { | 564 while (*sha1_hash) { |
| 898 bool ok = AddHash(*hash, &out->bad_static_spki_hashes); | 565 AddHash(*sha1_hash, &out->bad_static_spki_hashes); |
| 899 DCHECK(ok) << " failed to parse " << *hash; | 566 sha1_hash++; |
| 900 hash++; | |
| 901 } | 567 } |
| 902 } | 568 } |
| 903 } | 569 } |
| 904 return true; | 570 return true; |
| 905 } | 571 } |
| 906 } | 572 } |
| 907 return false; | 573 return false; |
| 908 } | 574 } |
| 909 | 575 |
| 910 #include "net/base/transport_security_state_static.h" | 576 #include "net/base/transport_security_state_static.h" |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 929 if (entry->length == canonicalized_host.size() - i && | 595 if (entry->length == canonicalized_host.size() - i && |
| 930 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) { | 596 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) { |
| 931 return entry; | 597 return entry; |
| 932 } | 598 } |
| 933 } | 599 } |
| 934 } | 600 } |
| 935 | 601 |
| 936 return NULL; | 602 return NULL; |
| 937 } | 603 } |
| 938 | 604 |
| 605 bool TransportSecurityState::AddHSTSHeader(const std::string& host, | |
| 606 const std::string& value) { | |
| 607 base::Time now = base::Time::Now(); | |
| 608 TransportSecurityState::DomainState domain_state; | |
| 609 if (ParseHSTSHeader(now, value, | |
| 610 &domain_state.upgrade_expiry, | |
| 611 &domain_state.include_subdomains)) { | |
|
Ryan Sleevi
2012/12/07 23:37:21
The wrapping here is inconsistent. Can you fit thi
unsafe
2012/12/08 09:22:42
Done.
| |
| 612 // Handle max-age == 0 | |
| 613 if (now == domain_state.upgrade_expiry) | |
| 614 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | |
| 615 else | |
| 616 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; | |
| 617 domain_state.created = now; | |
| 618 EnableHost(host, domain_state); | |
| 619 return true; | |
| 620 } | |
| 621 return false; | |
| 622 } | |
| 623 | |
| 624 bool TransportSecurityState::AddHPKPHeader(const std::string& host, | |
| 625 const std::string& value, | |
| 626 const SSLInfo& ssl_info) { | |
| 627 base::Time now = base::Time::Now(); | |
| 628 TransportSecurityState::DomainState domain_state; | |
| 629 if (ParseHPKPHeader(now, value, | |
| 630 ssl_info.public_key_hashes, | |
| 631 &domain_state.dynamic_spki_hashes_expiry, | |
| 632 &domain_state.dynamic_spki_hashes)) { | |
| 633 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | |
| 634 domain_state.created = now; | |
| 635 EnableHost(host, domain_state); | |
| 636 return true; | |
| 637 } | |
| 638 return false; | |
| 639 } | |
| 640 | |
| 939 // static | 641 // static |
| 940 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | 642 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, |
| 941 bool sni_enabled) { | 643 bool sni_enabled) { |
| 942 std::string canonicalized_host = CanonicalizeHost(host); | 644 std::string canonicalized_host = CanonicalizeHost(host); |
| 943 const struct HSTSPreload* entry = | 645 const struct HSTSPreload* entry = |
| 944 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 646 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
| 945 | 647 |
| 946 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 648 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
| 947 return true; | 649 return true; |
| 948 | 650 |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 974 } | 676 } |
| 975 | 677 |
| 976 DCHECK(entry); | 678 DCHECK(entry); |
| 977 DCHECK(entry->pins.required_hashes); | 679 DCHECK(entry->pins.required_hashes); |
| 978 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | 680 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); |
| 979 | 681 |
| 980 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | 682 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
| 981 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | 683 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); |
| 982 } | 684 } |
| 983 | 685 |
| 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( | 686 bool TransportSecurityState::GetStaticDomainState( |
| 1000 const std::string& canonicalized_host, | 687 const std::string& canonicalized_host, |
| 1001 bool sni_enabled, | 688 bool sni_enabled, |
| 1002 DomainState* out) { | 689 DomainState* out) { |
| 1003 DCHECK(CalledOnValidThread()); | 690 DCHECK(CalledOnValidThread()); |
| 1004 | 691 |
| 1005 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; | 692 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
| 1006 out->include_subdomains = false; | 693 out->include_subdomains = false; |
| 1007 | 694 |
| 1008 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 695 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 1033 void TransportSecurityState::AddOrUpdateEnabledHosts( | 720 void TransportSecurityState::AddOrUpdateEnabledHosts( |
| 1034 const std::string& hashed_host, const DomainState& state) { | 721 const std::string& hashed_host, const DomainState& state) { |
| 1035 enabled_hosts_[hashed_host] = state; | 722 enabled_hosts_[hashed_host] = state; |
| 1036 } | 723 } |
| 1037 | 724 |
| 1038 void TransportSecurityState::AddOrUpdateForcedHosts( | 725 void TransportSecurityState::AddOrUpdateForcedHosts( |
| 1039 const std::string& hashed_host, const DomainState& state) { | 726 const std::string& hashed_host, const DomainState& state) { |
| 1040 forced_hosts_[hashed_host] = state; | 727 forced_hosts_[hashed_host] = state; |
| 1041 } | 728 } |
| 1042 | 729 |
| 1043 static std::string HashesToBase64String( | |
| 1044 const HashValueVector& hashes) { | |
| 1045 std::vector<std::string> hashes_strs; | |
| 1046 for (HashValueVector::const_iterator | |
| 1047 i = hashes.begin(); i != hashes.end(); i++) { | |
| 1048 std::string s; | |
| 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 } | |
| 1054 | |
| 1055 return JoinString(hashes_strs, ','); | |
| 1056 } | |
| 1057 | |
| 1058 TransportSecurityState::DomainState::DomainState() | 730 TransportSecurityState::DomainState::DomainState() |
| 1059 : upgrade_mode(MODE_FORCE_HTTPS), | 731 : upgrade_mode(MODE_FORCE_HTTPS), |
| 1060 created(base::Time::Now()), | 732 created(base::Time::Now()), |
| 1061 include_subdomains(false) { | 733 include_subdomains(false) { |
| 1062 } | 734 } |
| 1063 | 735 |
| 1064 TransportSecurityState::DomainState::~DomainState() { | 736 TransportSecurityState::DomainState::~DomainState() { |
| 1065 } | 737 } |
| 1066 | 738 |
| 1067 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( | 739 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1110 return true; | 782 return true; |
| 1111 } | 783 } |
| 1112 | 784 |
| 1113 bool TransportSecurityState::DomainState::HasPins() const { | 785 bool TransportSecurityState::DomainState::HasPins() const { |
| 1114 return static_spki_hashes.size() > 0 || | 786 return static_spki_hashes.size() > 0 || |
| 1115 bad_static_spki_hashes.size() > 0 || | 787 bad_static_spki_hashes.size() > 0 || |
| 1116 dynamic_spki_hashes.size() > 0; | 788 dynamic_spki_hashes.size() > 0; |
| 1117 } | 789 } |
| 1118 | 790 |
| 1119 } // namespace | 791 } // namespace |
| OLD | NEW |