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 |