| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "components/ssl_errors/error_classification.h" | 5 #include "components/ssl_errors/error_classification.h" |
| 6 | 6 |
| 7 #include <limits.h> | 7 #include <limits.h> |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 | 9 |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 #include "base/win/windows_version.h" | 31 #include "base/win/windows_version.h" |
| 32 #endif | 32 #endif |
| 33 | 33 |
| 34 using base::Time; | 34 using base::Time; |
| 35 using base::TimeTicks; | 35 using base::TimeTicks; |
| 36 using base::TimeDelta; | 36 using base::TimeDelta; |
| 37 | 37 |
| 38 namespace ssl_errors { | 38 namespace ssl_errors { |
| 39 namespace { | 39 namespace { |
| 40 | 40 |
| 41 // Events for UMA. Do not reorder or change! | |
| 42 enum SSLInterstitialCause { | |
| 43 CLOCK_PAST, | |
| 44 CLOCK_FUTURE, | |
| 45 WWW_SUBDOMAIN_MATCH, | |
| 46 SUBDOMAIN_MATCH, | |
| 47 SUBDOMAIN_INVERSE_MATCH, | |
| 48 SUBDOMAIN_OUTSIDE_WILDCARD, | |
| 49 HOST_NAME_NOT_KNOWN_TLD, | |
| 50 LIKELY_MULTI_TENANT_HOSTING, | |
| 51 LOCALHOST, | |
| 52 PRIVATE_URL, | |
| 53 AUTHORITY_ERROR_CAPTIVE_PORTAL, // Deprecated in M47. | |
| 54 SELF_SIGNED, | |
| 55 EXPIRED_RECENTLY, | |
| 56 LIKELY_SAME_DOMAIN, | |
| 57 UNUSED_INTERSTITIAL_CAUSE_ENTRY, | |
| 58 }; | |
| 59 | |
| 60 void RecordSSLInterstitialCause(bool overridable, SSLInterstitialCause event) { | 41 void RecordSSLInterstitialCause(bool overridable, SSLInterstitialCause event) { |
| 61 if (overridable) { | 42 if (overridable) { |
| 62 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.overridable", event, | 43 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.overridable", event, |
| 63 UNUSED_INTERSTITIAL_CAUSE_ENTRY); | 44 SSL_INTERSTITIAL_CAUSE_MAX); |
| 64 } else { | 45 } else { |
| 65 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.nonoverridable", event, | 46 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.nonoverridable", event, |
| 66 UNUSED_INTERSTITIAL_CAUSE_ENTRY); | 47 SSL_INTERSTITIAL_CAUSE_MAX); |
| 67 } | 48 } |
| 68 } | 49 } |
| 69 | 50 |
| 70 std::vector<HostnameTokens> GetTokenizedDNSNames( | 51 std::vector<HostnameTokens> GetTokenizedDNSNames( |
| 71 const std::vector<std::string>& dns_names) { | 52 const std::vector<std::string>& dns_names) { |
| 72 std::vector<HostnameTokens> dns_name_tokens; | 53 std::vector<HostnameTokens> dns_name_tokens; |
| 73 for (const auto& dns_name : dns_names) { | 54 for (const auto& dns_name : dns_names) { |
| 74 HostnameTokens dns_name_token_single; | 55 HostnameTokens dns_name_token_single; |
| 75 if (dns_name.empty() || dns_name.find('\0') != std::string::npos || | 56 if (dns_name.empty() || dns_name.find('\0') != std::string::npos || |
| 76 !(HostNameHasKnownTLD(dns_name))) { | 57 !(HostNameHasKnownTLD(dns_name))) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 102 return -1; | 83 return -1; |
| 103 } | 84 } |
| 104 return diff_size; | 85 return diff_size; |
| 105 } | 86 } |
| 106 | 87 |
| 107 // We accept the inverse case for www for historical reasons. | 88 // We accept the inverse case for www for historical reasons. |
| 108 bool IsWWWSubDomainMatch(const GURL& request_url, | 89 bool IsWWWSubDomainMatch(const GURL& request_url, |
| 109 const net::X509Certificate& cert) { | 90 const net::X509Certificate& cert) { |
| 110 std::string www_host; | 91 std::string www_host; |
| 111 std::vector<std::string> dns_names; | 92 std::vector<std::string> dns_names; |
| 112 cert.GetDNSNames(&dns_names); | 93 cert.GetSubjectAltName(&dns_names, nullptr); |
| 113 return GetWWWSubDomainMatch(request_url, dns_names, &www_host); | 94 return GetWWWSubDomainMatch(request_url, dns_names, &www_host); |
| 114 } | 95 } |
| 115 | 96 |
| 116 // The time to use when doing build time operations in browser tests. | 97 // The time to use when doing build time operations in browser tests. |
| 117 base::LazyInstance<base::Time>::DestructorAtExit g_testing_build_time = | 98 base::LazyInstance<base::Time>::DestructorAtExit g_testing_build_time = |
| 118 LAZY_INSTANCE_INITIALIZER; | 99 LAZY_INSTANCE_INITIALIZER; |
| 119 | 100 |
| 120 } // namespace | 101 } // namespace |
| 121 | 102 |
| 122 static ssl_errors::ErrorInfo::ErrorType RecordErrorType(int cert_error) { | 103 static ssl_errors::ErrorInfo::ErrorType RecordErrorType(int cert_error) { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 142 // Note: not reached when displaying the bad clock interstitial. | 123 // Note: not reached when displaying the bad clock interstitial. |
| 143 // See |RecordUMAStatisticsForClockInterstitial| below. | 124 // See |RecordUMAStatisticsForClockInterstitial| below. |
| 144 if (cert.HasExpired() && | 125 if (cert.HasExpired() && |
| 145 (current_time - cert.valid_expiry()).InDays() < 28) { | 126 (current_time - cert.valid_expiry()).InDays() < 28) { |
| 146 RecordSSLInterstitialCause(overridable, EXPIRED_RECENTLY); | 127 RecordSSLInterstitialCause(overridable, EXPIRED_RECENTLY); |
| 147 } | 128 } |
| 148 break; | 129 break; |
| 149 } | 130 } |
| 150 case ssl_errors::ErrorInfo::CERT_COMMON_NAME_INVALID: { | 131 case ssl_errors::ErrorInfo::CERT_COMMON_NAME_INVALID: { |
| 151 std::string host_name = request_url.host(); | 132 std::string host_name = request_url.host(); |
| 133 std::vector<std::string> dns_names; |
| 134 cert.GetSubjectAltName(&dns_names, nullptr); |
| 135 std::vector<HostnameTokens> dns_name_tokens = |
| 136 GetTokenizedDNSNames(dns_names); |
| 137 |
| 138 if (dns_names.empty()) |
| 139 RecordSSLInterstitialCause(overridable, NO_SUBJECT_ALT_NAME); |
| 140 |
| 152 if (HostNameHasKnownTLD(host_name)) { | 141 if (HostNameHasKnownTLD(host_name)) { |
| 153 HostnameTokens host_name_tokens = Tokenize(host_name); | 142 HostnameTokens host_name_tokens = Tokenize(host_name); |
| 154 if (IsWWWSubDomainMatch(request_url, cert)) | 143 if (IsWWWSubDomainMatch(request_url, cert)) |
| 155 RecordSSLInterstitialCause(overridable, WWW_SUBDOMAIN_MATCH); | 144 RecordSSLInterstitialCause(overridable, WWW_SUBDOMAIN_MATCH2); |
| 156 if (IsSubDomainOutsideWildcard(request_url, cert)) | 145 if (IsSubDomainOutsideWildcard(request_url, cert)) |
| 157 RecordSSLInterstitialCause(overridable, SUBDOMAIN_OUTSIDE_WILDCARD); | 146 RecordSSLInterstitialCause(overridable, SUBDOMAIN_OUTSIDE_WILDCARD2); |
| 158 std::vector<std::string> dns_names; | |
| 159 cert.GetDNSNames(&dns_names); | |
| 160 std::vector<HostnameTokens> dns_name_tokens = | |
| 161 GetTokenizedDNSNames(dns_names); | |
| 162 if (NameUnderAnyNames(host_name_tokens, dns_name_tokens)) | 147 if (NameUnderAnyNames(host_name_tokens, dns_name_tokens)) |
| 163 RecordSSLInterstitialCause(overridable, SUBDOMAIN_MATCH); | 148 RecordSSLInterstitialCause(overridable, SUBDOMAIN_MATCH2); |
| 164 if (AnyNamesUnderName(dns_name_tokens, host_name_tokens)) | 149 if (AnyNamesUnderName(dns_name_tokens, host_name_tokens)) |
| 165 RecordSSLInterstitialCause(overridable, SUBDOMAIN_INVERSE_MATCH); | 150 RecordSSLInterstitialCause(overridable, SUBDOMAIN_INVERSE_MATCH2); |
| 166 if (IsCertLikelyFromMultiTenantHosting(request_url, cert)) | 151 if (IsCertLikelyFromMultiTenantHosting(request_url, cert)) |
| 167 RecordSSLInterstitialCause(overridable, LIKELY_MULTI_TENANT_HOSTING); | 152 RecordSSLInterstitialCause(overridable, LIKELY_MULTI_TENANT_HOSTING2); |
| 168 if (IsCertLikelyFromSameDomain(request_url, cert)) | 153 if (IsCertLikelyFromSameDomain(request_url, cert)) |
| 169 RecordSSLInterstitialCause(overridable, LIKELY_SAME_DOMAIN); | 154 RecordSSLInterstitialCause(overridable, LIKELY_SAME_DOMAIN2); |
| 170 } else { | 155 } else { |
| 171 RecordSSLInterstitialCause(overridable, HOST_NAME_NOT_KNOWN_TLD); | 156 RecordSSLInterstitialCause(overridable, HOST_NAME_NOT_KNOWN_TLD); |
| 172 } | 157 } |
| 173 break; | 158 break; |
| 174 } | 159 } |
| 175 case ssl_errors::ErrorInfo::CERT_AUTHORITY_INVALID: { | 160 case ssl_errors::ErrorInfo::CERT_AUTHORITY_INVALID: { |
| 176 const std::string& hostname = request_url.HostNoBrackets(); | 161 const std::string& hostname = request_url.HostNoBrackets(); |
| 177 if (net::IsLocalhost(hostname)) | 162 if (net::IsLocalhost(hostname)) |
| 178 RecordSSLInterstitialCause(overridable, LOCALHOST); | 163 RecordSSLInterstitialCause(overridable, LOCALHOST); |
| 179 if (IsHostnameNonUniqueOrDotless(hostname)) | 164 if (IsHostnameNonUniqueOrDotless(hostname)) |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 376 } | 361 } |
| 377 } | 362 } |
| 378 return row[str2.size()]; | 363 return row[str2.size()]; |
| 379 } | 364 } |
| 380 | 365 |
| 381 bool IsSubDomainOutsideWildcard(const GURL& request_url, | 366 bool IsSubDomainOutsideWildcard(const GURL& request_url, |
| 382 const net::X509Certificate& cert) { | 367 const net::X509Certificate& cert) { |
| 383 std::string host_name = request_url.host(); | 368 std::string host_name = request_url.host(); |
| 384 HostnameTokens host_name_tokens = Tokenize(host_name); | 369 HostnameTokens host_name_tokens = Tokenize(host_name); |
| 385 std::vector<std::string> dns_names; | 370 std::vector<std::string> dns_names; |
| 386 cert.GetDNSNames(&dns_names); | 371 cert.GetSubjectAltName(&dns_names, nullptr); |
| 387 bool result = false; | 372 bool result = false; |
| 388 | 373 |
| 389 // This method requires that the host name be longer than the dns name on | 374 // This method requires that the host name be longer than the dns name on |
| 390 // the certificate. | 375 // the certificate. |
| 391 for (const auto& dns_name : dns_names) { | 376 for (const auto& dns_name : dns_names) { |
| 392 if (dns_name.length() < 2 || dns_name.length() >= host_name.length() || | 377 if (dns_name.length() < 2 || dns_name.length() >= host_name.length() || |
| 393 dns_name.find('\0') != std::string::npos || | 378 dns_name.find('\0') != std::string::npos || |
| 394 !HostNameHasKnownTLD(dns_name) || dns_name[0] != '*' || | 379 !HostNameHasKnownTLD(dns_name) || dns_name[0] != '*' || |
| 395 dns_name[1] != '.') { | 380 dns_name[1] != '.') { |
| 396 continue; | 381 continue; |
| 397 } | 382 } |
| 398 | 383 |
| 399 // Move past the "*.". | 384 // Move past the "*.". |
| 400 std::string extracted_dns_name = dns_name.substr(2); | 385 std::string extracted_dns_name = dns_name.substr(2); |
| 401 if (FindSubdomainDifference(host_name_tokens, | 386 if (FindSubdomainDifference(host_name_tokens, |
| 402 Tokenize(extracted_dns_name)) == 2) { | 387 Tokenize(extracted_dns_name)) == 2) { |
| 403 return true; | 388 return true; |
| 404 } | 389 } |
| 405 } | 390 } |
| 406 return result; | 391 return result; |
| 407 } | 392 } |
| 408 | 393 |
| 409 bool IsCertLikelyFromMultiTenantHosting(const GURL& request_url, | 394 bool IsCertLikelyFromMultiTenantHosting(const GURL& request_url, |
| 410 const net::X509Certificate& cert) { | 395 const net::X509Certificate& cert) { |
| 411 std::string host_name = request_url.host(); | 396 std::string host_name = request_url.host(); |
| 412 std::vector<std::string> dns_names; | 397 std::vector<std::string> dns_names; |
| 413 std::vector<std::string> dns_names_domain; | 398 std::vector<std::string> dns_names_domain; |
| 414 cert.GetDNSNames(&dns_names); | 399 cert.GetSubjectAltName(&dns_names, nullptr); |
| 415 size_t dns_names_size = dns_names.size(); | 400 size_t dns_names_size = dns_names.size(); |
| 416 | 401 |
| 417 // If there is only 1 DNS name then it is definitely not a shared certificate. | 402 // If there is only 1 DNS name then it is definitely not a shared certificate. |
| 418 if (dns_names_size == 0 || dns_names_size == 1) | 403 if (dns_names_size == 0 || dns_names_size == 1) |
| 419 return false; | 404 return false; |
| 420 | 405 |
| 421 // Check to see if all the domains in the SAN field in the SSL certificate are | 406 // Check to see if all the domains in the SAN field in the SSL certificate are |
| 422 // the same or not. | 407 // the same or not. |
| 423 for (size_t i = 0; i < dns_names_size; ++i) { | 408 for (size_t i = 0; i < dns_names_size; ++i) { |
| 424 dns_names_domain.push_back( | 409 dns_names_domain.push_back( |
| (...skipping 26 matching lines...) Expand all Loading... |
| 451 return false; | 436 return false; |
| 452 } | 437 } |
| 453 } | 438 } |
| 454 return true; | 439 return true; |
| 455 } | 440 } |
| 456 | 441 |
| 457 bool IsCertLikelyFromSameDomain(const GURL& request_url, | 442 bool IsCertLikelyFromSameDomain(const GURL& request_url, |
| 458 const net::X509Certificate& cert) { | 443 const net::X509Certificate& cert) { |
| 459 std::string host_name = request_url.host(); | 444 std::string host_name = request_url.host(); |
| 460 std::vector<std::string> dns_names; | 445 std::vector<std::string> dns_names; |
| 461 cert.GetDNSNames(&dns_names); | 446 cert.GetSubjectAltName(&dns_names, nullptr); |
| 447 if (dns_names.empty()) |
| 448 return false; |
| 462 | 449 |
| 463 dns_names.push_back(host_name); | 450 dns_names.push_back(host_name); |
| 464 std::vector<std::string> dns_names_domain; | 451 std::vector<std::string> dns_names_domain; |
| 465 | 452 |
| 466 for (const std::string& dns_name : dns_names) { | 453 for (const std::string& dns_name : dns_names) { |
| 467 dns_names_domain.push_back( | 454 dns_names_domain.push_back( |
| 468 net::registry_controlled_domains::GetDomainAndRegistry( | 455 net::registry_controlled_domains::GetDomainAndRegistry( |
| 469 dns_name, | 456 dns_name, |
| 470 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); | 457 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); |
| 471 } | 458 } |
| 472 | 459 |
| 473 DCHECK(!dns_names_domain.empty()); | 460 DCHECK(!dns_names_domain.empty()); |
| 474 const std::string& host_name_domain = dns_names_domain.back(); | 461 const std::string& host_name_domain = dns_names_domain.back(); |
| 475 | 462 |
| 476 // Last element is the original domain. So, excluding it. | 463 // Last element is the original domain. So, excluding it. |
| 477 return std::find(dns_names_domain.begin(), dns_names_domain.end() - 1, | 464 return std::find(dns_names_domain.begin(), dns_names_domain.end() - 1, |
| 478 host_name_domain) != dns_names_domain.end() - 1; | 465 host_name_domain) != dns_names_domain.end() - 1; |
| 479 } | 466 } |
| 480 | 467 |
| 481 bool IsHostnameNonUniqueOrDotless(const std::string& hostname) { | 468 bool IsHostnameNonUniqueOrDotless(const std::string& hostname) { |
| 482 return net::IsHostnameNonUnique(hostname) || | 469 return net::IsHostnameNonUnique(hostname) || |
| 483 hostname.find('.') == std::string::npos; | 470 hostname.find('.') == std::string::npos; |
| 484 } | 471 } |
| 485 | 472 |
| 486 } // namespace ssl_errors | 473 } // namespace ssl_errors |
| OLD | NEW |