| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/base/transport_security_state.h" | |
| 6 | |
| 7 #if defined(USE_OPENSSL) | |
| 8 #include <openssl/ecdsa.h> | |
| 9 #include <openssl/ssl.h> | |
| 10 #else // !defined(USE_OPENSSL) | |
| 11 #include <cryptohi.h> | |
| 12 #include <hasht.h> | |
| 13 #include <keyhi.h> | |
| 14 #include <pk11pub.h> | |
| 15 #include <nspr.h> | |
| 16 #endif | |
| 17 | |
| 18 #include <algorithm> | |
| 19 | |
| 20 #include "base/base64.h" | |
| 21 #include "base/build_time.h" | |
| 22 #include "base/logging.h" | |
| 23 #include "base/memory/scoped_ptr.h" | |
| 24 #include "base/metrics/histogram.h" | |
| 25 #include "base/sha1.h" | |
| 26 #include "base/string_number_conversions.h" | |
| 27 #include "base/string_util.h" | |
| 28 #include "base/time.h" | |
| 29 #include "base/utf_string_conversions.h" | |
| 30 #include "base/values.h" | |
| 31 #include "crypto/sha2.h" | |
| 32 #include "googleurl/src/gurl.h" | |
| 33 #include "net/base/dns_util.h" | |
| 34 #include "net/base/ssl_info.h" | |
| 35 #include "net/base/x509_cert_types.h" | |
| 36 #include "net/base/x509_certificate.h" | |
| 37 #include "net/http/http_security_headers.h" | |
| 38 | |
| 39 #if defined(USE_OPENSSL) | |
| 40 #include "crypto/openssl_util.h" | |
| 41 #endif | |
| 42 | |
| 43 namespace net { | |
| 44 | |
| 45 namespace { | |
| 46 | |
| 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) { | |
| 58 char hashed[crypto::kSHA256Length]; | |
| 59 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | |
| 60 return std::string(hashed, sizeof(hashed)); | |
| 61 } | |
| 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 | |
| 86 TransportSecurityState::TransportSecurityState() | |
| 87 : delegate_(NULL) { | |
| 88 } | |
| 89 | |
| 90 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state) | |
| 91 : iterator_(state.enabled_hosts_.begin()), | |
| 92 end_(state.enabled_hosts_.end()) { | |
| 93 } | |
| 94 | |
| 95 TransportSecurityState::Iterator::~Iterator() {} | |
| 96 | |
| 97 void TransportSecurityState::SetDelegate( | |
| 98 TransportSecurityState::Delegate* delegate) { | |
| 99 delegate_ = delegate; | |
| 100 } | |
| 101 | |
| 102 void TransportSecurityState::EnableHost(const std::string& host, | |
| 103 const DomainState& state) { | |
| 104 DCHECK(CalledOnValidThread()); | |
| 105 | |
| 106 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 107 if (canonicalized_host.empty()) | |
| 108 return; | |
| 109 | |
| 110 DomainState existing_state; | |
| 111 | |
| 112 // Use the original creation date if we already have this host. (But note | |
| 113 // that statically-defined states have no |created| date. Therefore, we do | |
| 114 // not bother to search the SNI-only static states.) | |
| 115 DomainState state_copy(state); | |
| 116 if (GetDomainState(host, false /* sni_enabled */, &existing_state) && | |
| 117 !existing_state.created.is_null()) { | |
| 118 state_copy.created = existing_state.created; | |
| 119 } | |
| 120 | |
| 121 // No need to store this value since it is redundant. (|canonicalized_host| | |
| 122 // is the map key.) | |
| 123 state_copy.domain.clear(); | |
| 124 | |
| 125 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; | |
| 126 DirtyNotify(); | |
| 127 } | |
| 128 | |
| 129 bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) { | |
| 130 DCHECK(CalledOnValidThread()); | |
| 131 | |
| 132 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 133 if (canonicalized_host.empty()) | |
| 134 return false; | |
| 135 | |
| 136 DomainStateMap::iterator i = enabled_hosts_.find( | |
| 137 HashHost(canonicalized_host)); | |
| 138 if (i != enabled_hosts_.end()) { | |
| 139 enabled_hosts_.erase(i); | |
| 140 DirtyNotify(); | |
| 141 return true; | |
| 142 } | |
| 143 return false; | |
| 144 } | |
| 145 | |
| 146 bool TransportSecurityState::GetDomainState(const std::string& host, | |
| 147 bool sni_enabled, | |
| 148 DomainState* result) { | |
| 149 DCHECK(CalledOnValidThread()); | |
| 150 | |
| 151 DomainState state; | |
| 152 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 153 if (canonicalized_host.empty()) | |
| 154 return false; | |
| 155 | |
| 156 bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, | |
| 157 &state); | |
| 158 std::string canonicalized_preload = CanonicalizeHost(state.domain); | |
| 159 | |
| 160 base::Time current_time(base::Time::Now()); | |
| 161 | |
| 162 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | |
| 163 std::string host_sub_chunk(&canonicalized_host[i], | |
| 164 canonicalized_host.size() - i); | |
| 165 // Exact match of a preload always wins. | |
| 166 if (has_preload && host_sub_chunk == canonicalized_preload) { | |
| 167 *result = state; | |
| 168 return true; | |
| 169 } | |
| 170 | |
| 171 DomainStateMap::iterator j = | |
| 172 enabled_hosts_.find(HashHost(host_sub_chunk)); | |
| 173 if (j == enabled_hosts_.end()) | |
| 174 continue; | |
| 175 | |
| 176 if (current_time > j->second.upgrade_expiry && | |
| 177 current_time > j->second.dynamic_spki_hashes_expiry) { | |
| 178 enabled_hosts_.erase(j); | |
| 179 DirtyNotify(); | |
| 180 continue; | |
| 181 } | |
| 182 | |
| 183 state = j->second; | |
| 184 state.domain = DNSDomainToString(host_sub_chunk); | |
| 185 | |
| 186 // Succeed if we matched the domain exactly or if subdomain matches are | |
| 187 // allowed. | |
| 188 if (i == 0 || j->second.include_subdomains) { | |
| 189 *result = state; | |
| 190 return true; | |
| 191 } | |
| 192 | |
| 193 return false; | |
| 194 } | |
| 195 | |
| 196 return false; | |
| 197 } | |
| 198 | |
| 199 void TransportSecurityState::ClearDynamicData() { | |
| 200 enabled_hosts_.clear(); | |
| 201 } | |
| 202 | |
| 203 void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) { | |
| 204 DCHECK(CalledOnValidThread()); | |
| 205 | |
| 206 bool dirtied = false; | |
| 207 | |
| 208 DomainStateMap::iterator i = enabled_hosts_.begin(); | |
| 209 while (i != enabled_hosts_.end()) { | |
| 210 if (i->second.created >= time) { | |
| 211 dirtied = true; | |
| 212 enabled_hosts_.erase(i++); | |
| 213 } else { | |
| 214 i++; | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 if (dirtied) | |
| 219 DirtyNotify(); | |
| 220 } | |
| 221 | |
| 222 TransportSecurityState::~TransportSecurityState() {} | |
| 223 | |
| 224 void TransportSecurityState::DirtyNotify() { | |
| 225 DCHECK(CalledOnValidThread()); | |
| 226 | |
| 227 if (delegate_) | |
| 228 delegate_->StateIsDirty(this); | |
| 229 } | |
| 230 | |
| 231 // static | |
| 232 std::string TransportSecurityState::CanonicalizeHost(const std::string& host) { | |
| 233 // We cannot perform the operations as detailed in the spec here as |host| | |
| 234 // has already undergone IDN processing before it reached us. Thus, we check | |
| 235 // that there are no invalid characters in the host and lowercase the result. | |
| 236 | |
| 237 std::string new_host; | |
| 238 if (!DNSDomainFromDot(host, &new_host)) { | |
| 239 // DNSDomainFromDot can fail if any label is > 63 bytes or if the whole | |
| 240 // name is >255 bytes. However, search terms can have those properties. | |
| 241 return std::string(); | |
| 242 } | |
| 243 | |
| 244 for (size_t i = 0; new_host[i]; i += new_host[i] + 1) { | |
| 245 const unsigned label_length = static_cast<unsigned>(new_host[i]); | |
| 246 if (!label_length) | |
| 247 break; | |
| 248 | |
| 249 for (size_t j = 0; j < label_length; ++j) { | |
| 250 // RFC 3490, 4.1, step 3 | |
| 251 if (!IsSTD3ASCIIValidCharacter(new_host[i + 1 + j])) | |
| 252 return std::string(); | |
| 253 | |
| 254 new_host[i + 1 + j] = tolower(new_host[i + 1 + j]); | |
| 255 } | |
| 256 | |
| 257 // step 3(b) | |
| 258 if (new_host[i + 1] == '-' || | |
| 259 new_host[i + label_length] == '-') { | |
| 260 return std::string(); | |
| 261 } | |
| 262 } | |
| 263 | |
| 264 return new_host; | |
| 265 } | |
| 266 | |
| 267 // |ReportUMAOnPinFailure| uses these to report which domain was associated | |
| 268 // with the public key pinning failure. | |
| 269 // | |
| 270 // DO NOT CHANGE THE ORDERING OF THESE NAMES OR REMOVE ANY OF THEM. Add new | |
| 271 // domains at the END of the listing (but before DOMAIN_NUM_EVENTS). | |
| 272 enum SecondLevelDomainName { | |
| 273 DOMAIN_NOT_PINNED, | |
| 274 | |
| 275 DOMAIN_GOOGLE_COM, | |
| 276 DOMAIN_ANDROID_COM, | |
| 277 DOMAIN_GOOGLE_ANALYTICS_COM, | |
| 278 DOMAIN_GOOGLEPLEX_COM, | |
| 279 DOMAIN_YTIMG_COM, | |
| 280 DOMAIN_GOOGLEUSERCONTENT_COM, | |
| 281 DOMAIN_YOUTUBE_COM, | |
| 282 DOMAIN_GOOGLEAPIS_COM, | |
| 283 DOMAIN_GOOGLEADSERVICES_COM, | |
| 284 DOMAIN_GOOGLECODE_COM, | |
| 285 DOMAIN_APPSPOT_COM, | |
| 286 DOMAIN_GOOGLESYNDICATION_COM, | |
| 287 DOMAIN_DOUBLECLICK_NET, | |
| 288 DOMAIN_GSTATIC_COM, | |
| 289 DOMAIN_GMAIL_COM, | |
| 290 DOMAIN_GOOGLEMAIL_COM, | |
| 291 DOMAIN_GOOGLEGROUPS_COM, | |
| 292 | |
| 293 DOMAIN_TORPROJECT_ORG, | |
| 294 | |
| 295 DOMAIN_TWITTER_COM, | |
| 296 DOMAIN_TWIMG_COM, | |
| 297 | |
| 298 DOMAIN_AKAMAIHD_NET, | |
| 299 | |
| 300 DOMAIN_TOR2WEB_ORG, | |
| 301 | |
| 302 DOMAIN_YOUTU_BE, | |
| 303 DOMAIN_GOOGLECOMMERCE_COM, | |
| 304 DOMAIN_URCHIN_COM, | |
| 305 DOMAIN_GOO_GL, | |
| 306 DOMAIN_G_CO, | |
| 307 DOMAIN_GOOGLE_AC, | |
| 308 DOMAIN_GOOGLE_AD, | |
| 309 DOMAIN_GOOGLE_AE, | |
| 310 DOMAIN_GOOGLE_AF, | |
| 311 DOMAIN_GOOGLE_AG, | |
| 312 DOMAIN_GOOGLE_AM, | |
| 313 DOMAIN_GOOGLE_AS, | |
| 314 DOMAIN_GOOGLE_AT, | |
| 315 DOMAIN_GOOGLE_AZ, | |
| 316 DOMAIN_GOOGLE_BA, | |
| 317 DOMAIN_GOOGLE_BE, | |
| 318 DOMAIN_GOOGLE_BF, | |
| 319 DOMAIN_GOOGLE_BG, | |
| 320 DOMAIN_GOOGLE_BI, | |
| 321 DOMAIN_GOOGLE_BJ, | |
| 322 DOMAIN_GOOGLE_BS, | |
| 323 DOMAIN_GOOGLE_BY, | |
| 324 DOMAIN_GOOGLE_CA, | |
| 325 DOMAIN_GOOGLE_CAT, | |
| 326 DOMAIN_GOOGLE_CC, | |
| 327 DOMAIN_GOOGLE_CD, | |
| 328 DOMAIN_GOOGLE_CF, | |
| 329 DOMAIN_GOOGLE_CG, | |
| 330 DOMAIN_GOOGLE_CH, | |
| 331 DOMAIN_GOOGLE_CI, | |
| 332 DOMAIN_GOOGLE_CL, | |
| 333 DOMAIN_GOOGLE_CM, | |
| 334 DOMAIN_GOOGLE_CN, | |
| 335 DOMAIN_CO_AO, | |
| 336 DOMAIN_CO_BW, | |
| 337 DOMAIN_CO_CK, | |
| 338 DOMAIN_CO_CR, | |
| 339 DOMAIN_CO_HU, | |
| 340 DOMAIN_CO_ID, | |
| 341 DOMAIN_CO_IL, | |
| 342 DOMAIN_CO_IM, | |
| 343 DOMAIN_CO_IN, | |
| 344 DOMAIN_CO_JE, | |
| 345 DOMAIN_CO_JP, | |
| 346 DOMAIN_CO_KE, | |
| 347 DOMAIN_CO_KR, | |
| 348 DOMAIN_CO_LS, | |
| 349 DOMAIN_CO_MA, | |
| 350 DOMAIN_CO_MZ, | |
| 351 DOMAIN_CO_NZ, | |
| 352 DOMAIN_CO_TH, | |
| 353 DOMAIN_CO_TZ, | |
| 354 DOMAIN_CO_UG, | |
| 355 DOMAIN_CO_UK, | |
| 356 DOMAIN_CO_UZ, | |
| 357 DOMAIN_CO_VE, | |
| 358 DOMAIN_CO_VI, | |
| 359 DOMAIN_CO_ZA, | |
| 360 DOMAIN_CO_ZM, | |
| 361 DOMAIN_CO_ZW, | |
| 362 DOMAIN_COM_AF, | |
| 363 DOMAIN_COM_AG, | |
| 364 DOMAIN_COM_AI, | |
| 365 DOMAIN_COM_AR, | |
| 366 DOMAIN_COM_AU, | |
| 367 DOMAIN_COM_BD, | |
| 368 DOMAIN_COM_BH, | |
| 369 DOMAIN_COM_BN, | |
| 370 DOMAIN_COM_BO, | |
| 371 DOMAIN_COM_BR, | |
| 372 DOMAIN_COM_BY, | |
| 373 DOMAIN_COM_BZ, | |
| 374 DOMAIN_COM_CN, | |
| 375 DOMAIN_COM_CO, | |
| 376 DOMAIN_COM_CU, | |
| 377 DOMAIN_COM_CY, | |
| 378 DOMAIN_COM_DO, | |
| 379 DOMAIN_COM_EC, | |
| 380 DOMAIN_COM_EG, | |
| 381 DOMAIN_COM_ET, | |
| 382 DOMAIN_COM_FJ, | |
| 383 DOMAIN_COM_GE, | |
| 384 DOMAIN_COM_GH, | |
| 385 DOMAIN_COM_GI, | |
| 386 DOMAIN_COM_GR, | |
| 387 DOMAIN_COM_GT, | |
| 388 DOMAIN_COM_HK, | |
| 389 DOMAIN_COM_IQ, | |
| 390 DOMAIN_COM_JM, | |
| 391 DOMAIN_COM_JO, | |
| 392 DOMAIN_COM_KH, | |
| 393 DOMAIN_COM_KW, | |
| 394 DOMAIN_COM_LB, | |
| 395 DOMAIN_COM_LY, | |
| 396 DOMAIN_COM_MT, | |
| 397 DOMAIN_COM_MX, | |
| 398 DOMAIN_COM_MY, | |
| 399 DOMAIN_COM_NA, | |
| 400 DOMAIN_COM_NF, | |
| 401 DOMAIN_COM_NG, | |
| 402 DOMAIN_COM_NI, | |
| 403 DOMAIN_COM_NP, | |
| 404 DOMAIN_COM_NR, | |
| 405 DOMAIN_COM_OM, | |
| 406 DOMAIN_COM_PA, | |
| 407 DOMAIN_COM_PE, | |
| 408 DOMAIN_COM_PH, | |
| 409 DOMAIN_COM_PK, | |
| 410 DOMAIN_COM_PL, | |
| 411 DOMAIN_COM_PR, | |
| 412 DOMAIN_COM_PY, | |
| 413 DOMAIN_COM_QA, | |
| 414 DOMAIN_COM_RU, | |
| 415 DOMAIN_COM_SA, | |
| 416 DOMAIN_COM_SB, | |
| 417 DOMAIN_COM_SG, | |
| 418 DOMAIN_COM_SL, | |
| 419 DOMAIN_COM_SV, | |
| 420 DOMAIN_COM_TJ, | |
| 421 DOMAIN_COM_TN, | |
| 422 DOMAIN_COM_TR, | |
| 423 DOMAIN_COM_TW, | |
| 424 DOMAIN_COM_UA, | |
| 425 DOMAIN_COM_UY, | |
| 426 DOMAIN_COM_VC, | |
| 427 DOMAIN_COM_VE, | |
| 428 DOMAIN_COM_VN, | |
| 429 DOMAIN_GOOGLE_CV, | |
| 430 DOMAIN_GOOGLE_CZ, | |
| 431 DOMAIN_GOOGLE_DE, | |
| 432 DOMAIN_GOOGLE_DJ, | |
| 433 DOMAIN_GOOGLE_DK, | |
| 434 DOMAIN_GOOGLE_DM, | |
| 435 DOMAIN_GOOGLE_DZ, | |
| 436 DOMAIN_GOOGLE_EE, | |
| 437 DOMAIN_GOOGLE_ES, | |
| 438 DOMAIN_GOOGLE_FI, | |
| 439 DOMAIN_GOOGLE_FM, | |
| 440 DOMAIN_GOOGLE_FR, | |
| 441 DOMAIN_GOOGLE_GA, | |
| 442 DOMAIN_GOOGLE_GE, | |
| 443 DOMAIN_GOOGLE_GG, | |
| 444 DOMAIN_GOOGLE_GL, | |
| 445 DOMAIN_GOOGLE_GM, | |
| 446 DOMAIN_GOOGLE_GP, | |
| 447 DOMAIN_GOOGLE_GR, | |
| 448 DOMAIN_GOOGLE_GY, | |
| 449 DOMAIN_GOOGLE_HK, | |
| 450 DOMAIN_GOOGLE_HN, | |
| 451 DOMAIN_GOOGLE_HR, | |
| 452 DOMAIN_GOOGLE_HT, | |
| 453 DOMAIN_GOOGLE_HU, | |
| 454 DOMAIN_GOOGLE_IE, | |
| 455 DOMAIN_GOOGLE_IM, | |
| 456 DOMAIN_GOOGLE_INFO, | |
| 457 DOMAIN_GOOGLE_IQ, | |
| 458 DOMAIN_GOOGLE_IS, | |
| 459 DOMAIN_GOOGLE_IT, | |
| 460 DOMAIN_IT_AO, | |
| 461 DOMAIN_GOOGLE_JE, | |
| 462 DOMAIN_GOOGLE_JO, | |
| 463 DOMAIN_GOOGLE_JOBS, | |
| 464 DOMAIN_GOOGLE_JP, | |
| 465 DOMAIN_GOOGLE_KG, | |
| 466 DOMAIN_GOOGLE_KI, | |
| 467 DOMAIN_GOOGLE_KZ, | |
| 468 DOMAIN_GOOGLE_LA, | |
| 469 DOMAIN_GOOGLE_LI, | |
| 470 DOMAIN_GOOGLE_LK, | |
| 471 DOMAIN_GOOGLE_LT, | |
| 472 DOMAIN_GOOGLE_LU, | |
| 473 DOMAIN_GOOGLE_LV, | |
| 474 DOMAIN_GOOGLE_MD, | |
| 475 DOMAIN_GOOGLE_ME, | |
| 476 DOMAIN_GOOGLE_MG, | |
| 477 DOMAIN_GOOGLE_MK, | |
| 478 DOMAIN_GOOGLE_ML, | |
| 479 DOMAIN_GOOGLE_MN, | |
| 480 DOMAIN_GOOGLE_MS, | |
| 481 DOMAIN_GOOGLE_MU, | |
| 482 DOMAIN_GOOGLE_MV, | |
| 483 DOMAIN_GOOGLE_MW, | |
| 484 DOMAIN_GOOGLE_NE, | |
| 485 DOMAIN_NE_JP, | |
| 486 DOMAIN_GOOGLE_NET, | |
| 487 DOMAIN_GOOGLE_NL, | |
| 488 DOMAIN_GOOGLE_NO, | |
| 489 DOMAIN_GOOGLE_NR, | |
| 490 DOMAIN_GOOGLE_NU, | |
| 491 DOMAIN_OFF_AI, | |
| 492 DOMAIN_GOOGLE_PK, | |
| 493 DOMAIN_GOOGLE_PL, | |
| 494 DOMAIN_GOOGLE_PN, | |
| 495 DOMAIN_GOOGLE_PS, | |
| 496 DOMAIN_GOOGLE_PT, | |
| 497 DOMAIN_GOOGLE_RO, | |
| 498 DOMAIN_GOOGLE_RS, | |
| 499 DOMAIN_GOOGLE_RU, | |
| 500 DOMAIN_GOOGLE_RW, | |
| 501 DOMAIN_GOOGLE_SC, | |
| 502 DOMAIN_GOOGLE_SE, | |
| 503 DOMAIN_GOOGLE_SH, | |
| 504 DOMAIN_GOOGLE_SI, | |
| 505 DOMAIN_GOOGLE_SK, | |
| 506 DOMAIN_GOOGLE_SM, | |
| 507 DOMAIN_GOOGLE_SN, | |
| 508 DOMAIN_GOOGLE_SO, | |
| 509 DOMAIN_GOOGLE_ST, | |
| 510 DOMAIN_GOOGLE_TD, | |
| 511 DOMAIN_GOOGLE_TG, | |
| 512 DOMAIN_GOOGLE_TK, | |
| 513 DOMAIN_GOOGLE_TL, | |
| 514 DOMAIN_GOOGLE_TM, | |
| 515 DOMAIN_GOOGLE_TN, | |
| 516 DOMAIN_GOOGLE_TO, | |
| 517 DOMAIN_GOOGLE_TP, | |
| 518 DOMAIN_GOOGLE_TT, | |
| 519 DOMAIN_GOOGLE_US, | |
| 520 DOMAIN_GOOGLE_UZ, | |
| 521 DOMAIN_GOOGLE_VG, | |
| 522 DOMAIN_GOOGLE_VU, | |
| 523 DOMAIN_GOOGLE_WS, | |
| 524 | |
| 525 DOMAIN_CHROMIUM_ORG, | |
| 526 | |
| 527 DOMAIN_CRYPTO_CAT, | |
| 528 | |
| 529 // Boundary value for UMA_HISTOGRAM_ENUMERATION: | |
| 530 DOMAIN_NUM_EVENTS | |
| 531 }; | |
| 532 | |
| 533 // PublicKeyPins contains a number of SubjectPublicKeyInfo hashes for a site. | |
| 534 // The validated certificate chain for the site must not include any of | |
| 535 // |excluded_hashes| and must include one or more of |required_hashes|. | |
| 536 struct PublicKeyPins { | |
| 537 const char* const* required_hashes; | |
| 538 const char* const* excluded_hashes; | |
| 539 }; | |
| 540 | |
| 541 struct HSTSPreload { | |
| 542 uint8 length; | |
| 543 bool include_subdomains; | |
| 544 char dns_name[34]; | |
| 545 bool https_required; | |
| 546 PublicKeyPins pins; | |
| 547 SecondLevelDomainName second_level_domain_name; | |
| 548 }; | |
| 549 | |
| 550 static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries, | |
| 551 const std::string& canonicalized_host, size_t i, | |
| 552 TransportSecurityState::DomainState* out, bool* ret) { | |
| 553 for (size_t j = 0; j < num_entries; j++) { | |
| 554 if (entries[j].length == canonicalized_host.size() - i && | |
| 555 memcmp(entries[j].dns_name, &canonicalized_host[i], | |
| 556 entries[j].length) == 0) { | |
| 557 if (!entries[j].include_subdomains && i != 0) { | |
| 558 *ret = false; | |
| 559 } else { | |
| 560 out->include_subdomains = entries[j].include_subdomains; | |
| 561 *ret = true; | |
| 562 if (!entries[j].https_required) | |
| 563 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; | |
| 564 if (entries[j].pins.required_hashes) { | |
| 565 const char* const* sha1_hash = entries[j].pins.required_hashes; | |
| 566 while (*sha1_hash) { | |
| 567 AddHash(*sha1_hash, &out->static_spki_hashes); | |
| 568 sha1_hash++; | |
| 569 } | |
| 570 } | |
| 571 if (entries[j].pins.excluded_hashes) { | |
| 572 const char* const* sha1_hash = entries[j].pins.excluded_hashes; | |
| 573 while (*sha1_hash) { | |
| 574 AddHash(*sha1_hash, &out->bad_static_spki_hashes); | |
| 575 sha1_hash++; | |
| 576 } | |
| 577 } | |
| 578 } | |
| 579 return true; | |
| 580 } | |
| 581 } | |
| 582 return false; | |
| 583 } | |
| 584 | |
| 585 #include "net/base/transport_security_state_static.h" | |
| 586 | |
| 587 // Returns the HSTSPreload entry for the |canonicalized_host| in |entries|, | |
| 588 // or NULL if there is none. Prefers exact hostname matches to those that | |
| 589 // match only because HSTSPreload.include_subdomains is true. | |
| 590 // | |
| 591 // |canonicalized_host| should be the hostname as canonicalized by | |
| 592 // CanonicalizeHost. | |
| 593 static const struct HSTSPreload* GetHSTSPreload( | |
| 594 const std::string& canonicalized_host, | |
| 595 const struct HSTSPreload* entries, | |
| 596 size_t num_entries) { | |
| 597 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | |
| 598 for (size_t j = 0; j < num_entries; j++) { | |
| 599 const struct HSTSPreload* entry = entries + j; | |
| 600 | |
| 601 if (i != 0 && !entry->include_subdomains) | |
| 602 continue; | |
| 603 | |
| 604 if (entry->length == canonicalized_host.size() - i && | |
| 605 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) { | |
| 606 return entry; | |
| 607 } | |
| 608 } | |
| 609 } | |
| 610 | |
| 611 return NULL; | |
| 612 } | |
| 613 | |
| 614 bool TransportSecurityState::AddHSTSHeader(const std::string& host, | |
| 615 const std::string& value) { | |
| 616 base::Time now = base::Time::Now(); | |
| 617 TransportSecurityState::DomainState domain_state; | |
| 618 if (ParseHSTSHeader(now, value, &domain_state.upgrade_expiry, | |
| 619 &domain_state.include_subdomains)) { | |
| 620 // Handle max-age == 0 | |
| 621 if (now == domain_state.upgrade_expiry) | |
| 622 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | |
| 623 else | |
| 624 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; | |
| 625 domain_state.created = now; | |
| 626 EnableHost(host, domain_state); | |
| 627 return true; | |
| 628 } | |
| 629 return false; | |
| 630 } | |
| 631 | |
| 632 bool TransportSecurityState::AddHPKPHeader(const std::string& host, | |
| 633 const std::string& value, | |
| 634 const SSLInfo& ssl_info) { | |
| 635 base::Time now = base::Time::Now(); | |
| 636 TransportSecurityState::DomainState domain_state; | |
| 637 if (ParseHPKPHeader(now, value, ssl_info.public_key_hashes, | |
| 638 &domain_state.dynamic_spki_hashes_expiry, | |
| 639 &domain_state.dynamic_spki_hashes)) { | |
| 640 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | |
| 641 domain_state.created = now; | |
| 642 EnableHost(host, domain_state); | |
| 643 return true; | |
| 644 } | |
| 645 return false; | |
| 646 } | |
| 647 | |
| 648 bool TransportSecurityState::AddHSTS(const std::string& host, | |
| 649 const base::Time& expiry, | |
| 650 bool include_subdomains) { | |
| 651 // Copy-and-modify the existing DomainState for this host (if any). | |
| 652 TransportSecurityState::DomainState domain_state; | |
| 653 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 654 const std::string hashed_host = HashHost(canonicalized_host); | |
| 655 DomainStateMap::const_iterator i = enabled_hosts_.find( | |
| 656 hashed_host); | |
| 657 if (i != enabled_hosts_.end()) | |
| 658 domain_state = i->second; | |
| 659 | |
| 660 domain_state.created = base::Time::Now(); | |
| 661 domain_state.include_subdomains = include_subdomains; | |
| 662 domain_state.upgrade_expiry = expiry; | |
| 663 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; | |
| 664 EnableHost(host, domain_state); | |
| 665 return true; | |
| 666 } | |
| 667 | |
| 668 bool TransportSecurityState::AddHPKP(const std::string& host, | |
| 669 const base::Time& expiry, | |
| 670 bool include_subdomains, | |
| 671 const HashValueVector& hashes) { | |
| 672 // Copy-and-modify the existing DomainState for this host (if any). | |
| 673 TransportSecurityState::DomainState domain_state; | |
| 674 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 675 const std::string hashed_host = HashHost(canonicalized_host); | |
| 676 DomainStateMap::const_iterator i = enabled_hosts_.find( | |
| 677 hashed_host); | |
| 678 if (i != enabled_hosts_.end()) | |
| 679 domain_state = i->second; | |
| 680 | |
| 681 domain_state.created = base::Time::Now(); | |
| 682 domain_state.include_subdomains = include_subdomains; | |
| 683 domain_state.dynamic_spki_hashes_expiry = expiry; | |
| 684 domain_state.dynamic_spki_hashes = hashes; | |
| 685 EnableHost(host, domain_state); | |
| 686 return true; | |
| 687 } | |
| 688 | |
| 689 // static | |
| 690 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | |
| 691 bool sni_enabled) { | |
| 692 std::string canonicalized_host = CanonicalizeHost(host); | |
| 693 const struct HSTSPreload* entry = | |
| 694 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | |
| 695 | |
| 696 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | |
| 697 return true; | |
| 698 | |
| 699 if (sni_enabled) { | |
| 700 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | |
| 701 kNumPreloadedSNISTS); | |
| 702 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | |
| 703 return true; | |
| 704 } | |
| 705 | |
| 706 return false; | |
| 707 } | |
| 708 | |
| 709 // static | |
| 710 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | |
| 711 std::string canonicalized_host = CanonicalizeHost(host); | |
| 712 | |
| 713 const struct HSTSPreload* entry = | |
| 714 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | |
| 715 | |
| 716 if (!entry) { | |
| 717 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | |
| 718 kNumPreloadedSNISTS); | |
| 719 } | |
| 720 | |
| 721 if (!entry) { | |
| 722 // We don't care to report pin failures for dynamic pins. | |
| 723 return; | |
| 724 } | |
| 725 | |
| 726 DCHECK(entry); | |
| 727 DCHECK(entry->pins.required_hashes); | |
| 728 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | |
| 729 | |
| 730 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | |
| 731 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | |
| 732 } | |
| 733 | |
| 734 // static | |
| 735 bool TransportSecurityState::IsBuildTimely() { | |
| 736 const base::Time build_time = base::GetBuildTime(); | |
| 737 // We consider built-in information to be timely for 10 weeks. | |
| 738 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; | |
| 739 } | |
| 740 | |
| 741 bool TransportSecurityState::GetStaticDomainState( | |
| 742 const std::string& canonicalized_host, | |
| 743 bool sni_enabled, | |
| 744 DomainState* out) { | |
| 745 DCHECK(CalledOnValidThread()); | |
| 746 | |
| 747 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; | |
| 748 out->include_subdomains = false; | |
| 749 | |
| 750 const bool is_build_timely = IsBuildTimely(); | |
| 751 | |
| 752 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | |
| 753 std::string host_sub_chunk(&canonicalized_host[i], | |
| 754 canonicalized_host.size() - i); | |
| 755 out->domain = DNSDomainToString(host_sub_chunk); | |
| 756 std::string hashed_host(HashHost(host_sub_chunk)); | |
| 757 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { | |
| 758 *out = forced_hosts_[hashed_host]; | |
| 759 out->domain = DNSDomainToString(host_sub_chunk); | |
| 760 return true; | |
| 761 } | |
| 762 bool ret; | |
| 763 if (is_build_timely && | |
| 764 HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, | |
| 765 &ret)) { | |
| 766 return ret; | |
| 767 } | |
| 768 if (sni_enabled && | |
| 769 is_build_timely && | |
| 770 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, | |
| 771 out, &ret)) { | |
| 772 return ret; | |
| 773 } | |
| 774 } | |
| 775 | |
| 776 return false; | |
| 777 } | |
| 778 | |
| 779 void TransportSecurityState::AddOrUpdateEnabledHosts( | |
| 780 const std::string& hashed_host, const DomainState& state) { | |
| 781 enabled_hosts_[hashed_host] = state; | |
| 782 } | |
| 783 | |
| 784 void TransportSecurityState::AddOrUpdateForcedHosts( | |
| 785 const std::string& hashed_host, const DomainState& state) { | |
| 786 forced_hosts_[hashed_host] = state; | |
| 787 } | |
| 788 | |
| 789 TransportSecurityState::DomainState::DomainState() | |
| 790 : upgrade_mode(MODE_FORCE_HTTPS), | |
| 791 created(base::Time::Now()), | |
| 792 include_subdomains(false) { | |
| 793 } | |
| 794 | |
| 795 TransportSecurityState::DomainState::~DomainState() { | |
| 796 } | |
| 797 | |
| 798 bool TransportSecurityState::DomainState::CheckPublicKeyPins( | |
| 799 const HashValueVector& hashes) const { | |
| 800 // Validate that hashes is not empty. By the time this code is called (in | |
| 801 // production), that should never happen, but it's good to be defensive. | |
| 802 // And, hashes *can* be empty in some test scenarios. | |
| 803 if (hashes.empty()) { | |
| 804 LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned " | |
| 805 "domain " << domain; | |
| 806 return false; | |
| 807 } | |
| 808 | |
| 809 if (HashesIntersect(bad_static_spki_hashes, hashes)) { | |
| 810 LOG(ERROR) << "Rejecting public key chain for domain " << domain | |
| 811 << ". Validated chain: " << HashesToBase64String(hashes) | |
| 812 << ", matches one or more bad hashes: " | |
| 813 << HashesToBase64String(bad_static_spki_hashes); | |
| 814 return false; | |
| 815 } | |
| 816 | |
| 817 // If there are no pins, then any valid chain is acceptable. | |
| 818 if (dynamic_spki_hashes.empty() && static_spki_hashes.empty()) | |
| 819 return true; | |
| 820 | |
| 821 if (HashesIntersect(dynamic_spki_hashes, hashes) || | |
| 822 HashesIntersect(static_spki_hashes, hashes)) { | |
| 823 return true; | |
| 824 } | |
| 825 | |
| 826 LOG(ERROR) << "Rejecting public key chain for domain " << domain | |
| 827 << ". Validated chain: " << HashesToBase64String(hashes) | |
| 828 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) | |
| 829 << " or: " << HashesToBase64String(static_spki_hashes); | |
| 830 return false; | |
| 831 } | |
| 832 | |
| 833 bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const { | |
| 834 return upgrade_mode == MODE_FORCE_HTTPS; | |
| 835 } | |
| 836 | |
| 837 bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const { | |
| 838 return true; | |
| 839 } | |
| 840 | |
| 841 bool TransportSecurityState::DomainState::Equals( | |
| 842 const DomainState& other) const { | |
| 843 // TODO(palmer): Implement this | |
| 844 (void) other; | |
| 845 return true; | |
| 846 } | |
| 847 | |
| 848 bool TransportSecurityState::DomainState::HasPublicKeyPins() const { | |
| 849 return static_spki_hashes.size() > 0 || | |
| 850 bad_static_spki_hashes.size() > 0 || | |
| 851 dynamic_spki_hashes.size() > 0; | |
| 852 } | |
| 853 | |
| 854 } // namespace | |
| OLD | NEW |