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> g_testing_build_time = LAZY_INSTANCE_INITIALIZER; | 98 base::LazyInstance<base::Time> g_testing_build_time = LAZY_INSTANCE_INITIALIZER; |
118 | 99 |
119 } // namespace | 100 } // namespace |
120 | 101 |
121 static ssl_errors::ErrorInfo::ErrorType RecordErrorType(int cert_error) { | 102 static ssl_errors::ErrorInfo::ErrorType RecordErrorType(int cert_error) { |
122 ssl_errors::ErrorInfo::ErrorType error_type = | 103 ssl_errors::ErrorInfo::ErrorType error_type = |
(...skipping 18 matching lines...) Expand all Loading... |
141 // Note: not reached when displaying the bad clock interstitial. | 122 // Note: not reached when displaying the bad clock interstitial. |
142 // See |RecordUMAStatisticsForClockInterstitial| below. | 123 // See |RecordUMAStatisticsForClockInterstitial| below. |
143 if (cert.HasExpired() && | 124 if (cert.HasExpired() && |
144 (current_time - cert.valid_expiry()).InDays() < 28) { | 125 (current_time - cert.valid_expiry()).InDays() < 28) { |
145 RecordSSLInterstitialCause(overridable, EXPIRED_RECENTLY); | 126 RecordSSLInterstitialCause(overridable, EXPIRED_RECENTLY); |
146 } | 127 } |
147 break; | 128 break; |
148 } | 129 } |
149 case ssl_errors::ErrorInfo::CERT_COMMON_NAME_INVALID: { | 130 case ssl_errors::ErrorInfo::CERT_COMMON_NAME_INVALID: { |
150 std::string host_name = request_url.host(); | 131 std::string host_name = request_url.host(); |
| 132 std::vector<std::string> dns_names; |
| 133 cert.GetSubjectAltName(&dns_names, nullptr); |
| 134 std::vector<HostnameTokens> dns_name_tokens = |
| 135 GetTokenizedDNSNames(dns_names); |
| 136 |
| 137 if (dns_names.empty()) |
| 138 RecordSSLInterstitialCause(overridable, NO_SUBJECT_ALT_NAME); |
| 139 |
151 if (HostNameHasKnownTLD(host_name)) { | 140 if (HostNameHasKnownTLD(host_name)) { |
152 HostnameTokens host_name_tokens = Tokenize(host_name); | 141 HostnameTokens host_name_tokens = Tokenize(host_name); |
153 if (IsWWWSubDomainMatch(request_url, cert)) | 142 if (IsWWWSubDomainMatch(request_url, cert)) |
154 RecordSSLInterstitialCause(overridable, WWW_SUBDOMAIN_MATCH); | 143 RecordSSLInterstitialCause(overridable, WWW_SUBDOMAIN_MATCH2); |
155 if (IsSubDomainOutsideWildcard(request_url, cert)) | 144 if (IsSubDomainOutsideWildcard(request_url, cert)) |
156 RecordSSLInterstitialCause(overridable, SUBDOMAIN_OUTSIDE_WILDCARD); | 145 RecordSSLInterstitialCause(overridable, SUBDOMAIN_OUTSIDE_WILDCARD2); |
157 std::vector<std::string> dns_names; | |
158 cert.GetDNSNames(&dns_names); | |
159 std::vector<HostnameTokens> dns_name_tokens = | |
160 GetTokenizedDNSNames(dns_names); | |
161 if (NameUnderAnyNames(host_name_tokens, dns_name_tokens)) | 146 if (NameUnderAnyNames(host_name_tokens, dns_name_tokens)) |
162 RecordSSLInterstitialCause(overridable, SUBDOMAIN_MATCH); | 147 RecordSSLInterstitialCause(overridable, SUBDOMAIN_MATCH2); |
163 if (AnyNamesUnderName(dns_name_tokens, host_name_tokens)) | 148 if (AnyNamesUnderName(dns_name_tokens, host_name_tokens)) |
164 RecordSSLInterstitialCause(overridable, SUBDOMAIN_INVERSE_MATCH); | 149 RecordSSLInterstitialCause(overridable, SUBDOMAIN_INVERSE_MATCH2); |
165 if (IsCertLikelyFromMultiTenantHosting(request_url, cert)) | 150 if (IsCertLikelyFromMultiTenantHosting(request_url, cert)) |
166 RecordSSLInterstitialCause(overridable, LIKELY_MULTI_TENANT_HOSTING); | 151 RecordSSLInterstitialCause(overridable, LIKELY_MULTI_TENANT_HOSTING2); |
167 if (IsCertLikelyFromSameDomain(request_url, cert)) | 152 if (IsCertLikelyFromSameDomain(request_url, cert)) |
168 RecordSSLInterstitialCause(overridable, LIKELY_SAME_DOMAIN); | 153 RecordSSLInterstitialCause(overridable, LIKELY_SAME_DOMAIN2); |
169 } else { | 154 } else { |
170 RecordSSLInterstitialCause(overridable, HOST_NAME_NOT_KNOWN_TLD); | 155 RecordSSLInterstitialCause(overridable, HOST_NAME_NOT_KNOWN_TLD); |
171 } | 156 } |
172 break; | 157 break; |
173 } | 158 } |
174 case ssl_errors::ErrorInfo::CERT_AUTHORITY_INVALID: { | 159 case ssl_errors::ErrorInfo::CERT_AUTHORITY_INVALID: { |
175 const std::string& hostname = request_url.HostNoBrackets(); | 160 const std::string& hostname = request_url.HostNoBrackets(); |
176 if (net::IsLocalhost(hostname)) | 161 if (net::IsLocalhost(hostname)) |
177 RecordSSLInterstitialCause(overridable, LOCALHOST); | 162 RecordSSLInterstitialCause(overridable, LOCALHOST); |
178 if (IsHostnameNonUniqueOrDotless(hostname)) | 163 if (IsHostnameNonUniqueOrDotless(hostname)) |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
375 } | 360 } |
376 } | 361 } |
377 return row[str2.size()]; | 362 return row[str2.size()]; |
378 } | 363 } |
379 | 364 |
380 bool IsSubDomainOutsideWildcard(const GURL& request_url, | 365 bool IsSubDomainOutsideWildcard(const GURL& request_url, |
381 const net::X509Certificate& cert) { | 366 const net::X509Certificate& cert) { |
382 std::string host_name = request_url.host(); | 367 std::string host_name = request_url.host(); |
383 HostnameTokens host_name_tokens = Tokenize(host_name); | 368 HostnameTokens host_name_tokens = Tokenize(host_name); |
384 std::vector<std::string> dns_names; | 369 std::vector<std::string> dns_names; |
385 cert.GetDNSNames(&dns_names); | 370 cert.GetSubjectAltName(&dns_names, nullptr); |
386 bool result = false; | 371 bool result = false; |
387 | 372 |
388 // This method requires that the host name be longer than the dns name on | 373 // This method requires that the host name be longer than the dns name on |
389 // the certificate. | 374 // the certificate. |
390 for (const auto& dns_name : dns_names) { | 375 for (const auto& dns_name : dns_names) { |
391 if (dns_name.length() < 2 || dns_name.length() >= host_name.length() || | 376 if (dns_name.length() < 2 || dns_name.length() >= host_name.length() || |
392 dns_name.find('\0') != std::string::npos || | 377 dns_name.find('\0') != std::string::npos || |
393 !HostNameHasKnownTLD(dns_name) || dns_name[0] != '*' || | 378 !HostNameHasKnownTLD(dns_name) || dns_name[0] != '*' || |
394 dns_name[1] != '.') { | 379 dns_name[1] != '.') { |
395 continue; | 380 continue; |
396 } | 381 } |
397 | 382 |
398 // Move past the "*.". | 383 // Move past the "*.". |
399 std::string extracted_dns_name = dns_name.substr(2); | 384 std::string extracted_dns_name = dns_name.substr(2); |
400 if (FindSubdomainDifference(host_name_tokens, | 385 if (FindSubdomainDifference(host_name_tokens, |
401 Tokenize(extracted_dns_name)) == 2) { | 386 Tokenize(extracted_dns_name)) == 2) { |
402 return true; | 387 return true; |
403 } | 388 } |
404 } | 389 } |
405 return result; | 390 return result; |
406 } | 391 } |
407 | 392 |
408 bool IsCertLikelyFromMultiTenantHosting(const GURL& request_url, | 393 bool IsCertLikelyFromMultiTenantHosting(const GURL& request_url, |
409 const net::X509Certificate& cert) { | 394 const net::X509Certificate& cert) { |
410 std::string host_name = request_url.host(); | 395 std::string host_name = request_url.host(); |
411 std::vector<std::string> dns_names; | 396 std::vector<std::string> dns_names; |
412 std::vector<std::string> dns_names_domain; | 397 std::vector<std::string> dns_names_domain; |
413 cert.GetDNSNames(&dns_names); | 398 cert.GetSubjectAltName(&dns_names, nullptr); |
414 size_t dns_names_size = dns_names.size(); | 399 size_t dns_names_size = dns_names.size(); |
415 | 400 |
416 // If there is only 1 DNS name then it is definitely not a shared certificate. | 401 // If there is only 1 DNS name then it is definitely not a shared certificate. |
417 if (dns_names_size == 0 || dns_names_size == 1) | 402 if (dns_names_size == 0 || dns_names_size == 1) |
418 return false; | 403 return false; |
419 | 404 |
420 // Check to see if all the domains in the SAN field in the SSL certificate are | 405 // Check to see if all the domains in the SAN field in the SSL certificate are |
421 // the same or not. | 406 // the same or not. |
422 for (size_t i = 0; i < dns_names_size; ++i) { | 407 for (size_t i = 0; i < dns_names_size; ++i) { |
423 dns_names_domain.push_back( | 408 dns_names_domain.push_back( |
(...skipping 26 matching lines...) Expand all Loading... |
450 return false; | 435 return false; |
451 } | 436 } |
452 } | 437 } |
453 return true; | 438 return true; |
454 } | 439 } |
455 | 440 |
456 bool IsCertLikelyFromSameDomain(const GURL& request_url, | 441 bool IsCertLikelyFromSameDomain(const GURL& request_url, |
457 const net::X509Certificate& cert) { | 442 const net::X509Certificate& cert) { |
458 std::string host_name = request_url.host(); | 443 std::string host_name = request_url.host(); |
459 std::vector<std::string> dns_names; | 444 std::vector<std::string> dns_names; |
460 cert.GetDNSNames(&dns_names); | 445 cert.GetSubjectAltName(&dns_names, nullptr); |
| 446 if (dns_names.empty()) |
| 447 return false; |
461 | 448 |
462 dns_names.push_back(host_name); | 449 dns_names.push_back(host_name); |
463 std::vector<std::string> dns_names_domain; | 450 std::vector<std::string> dns_names_domain; |
464 | 451 |
465 for (const std::string& dns_name : dns_names) { | 452 for (const std::string& dns_name : dns_names) { |
466 dns_names_domain.push_back( | 453 dns_names_domain.push_back( |
467 net::registry_controlled_domains::GetDomainAndRegistry( | 454 net::registry_controlled_domains::GetDomainAndRegistry( |
468 dns_name, | 455 dns_name, |
469 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); | 456 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); |
470 } | 457 } |
471 | 458 |
472 DCHECK(!dns_names_domain.empty()); | 459 DCHECK(!dns_names_domain.empty()); |
473 const std::string& host_name_domain = dns_names_domain.back(); | 460 const std::string& host_name_domain = dns_names_domain.back(); |
474 | 461 |
475 // Last element is the original domain. So, excluding it. | 462 // Last element is the original domain. So, excluding it. |
476 return std::find(dns_names_domain.begin(), dns_names_domain.end() - 1, | 463 return std::find(dns_names_domain.begin(), dns_names_domain.end() - 1, |
477 host_name_domain) != dns_names_domain.end() - 1; | 464 host_name_domain) != dns_names_domain.end() - 1; |
478 } | 465 } |
479 | 466 |
480 bool IsHostnameNonUniqueOrDotless(const std::string& hostname) { | 467 bool IsHostnameNonUniqueOrDotless(const std::string& hostname) { |
481 return net::IsHostnameNonUnique(hostname) || | 468 return net::IsHostnameNonUnique(hostname) || |
482 hostname.find('.') == std::string::npos; | 469 hostname.find('.') == std::string::npos; |
483 } | 470 } |
484 | 471 |
485 } // namespace ssl_errors | 472 } // namespace ssl_errors |
OLD | NEW |