Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: components/ssl_errors/error_classification.cc

Issue 2777383002: Update SSL error handling code to account for Subject CN deprecation (Closed)
Patch Set: Address Mark Feedback Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « components/ssl_errors/error_classification.h ('k') | components/ssl_errors/error_classification_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698