| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 <vector> | 5 #include <vector> |
| 6 | 6 |
| 7 #include "chrome/browser/ssl/ssl_error_classification.h" | 7 #include "chrome/browser/ssl/ssl_error_classification.h" |
| 8 | 8 |
| 9 #include "base/build_time.h" | 9 #include "base/build_time.h" |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram_macros.h" |
| 11 #include "base/strings/string_split.h" | 11 #include "base/strings/string_split.h" |
| 12 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
| 13 #include "base/time/time.h" | 13 #include "base/time/time.h" |
| 14 #include "chrome/browser/browser_process.h" | |
| 15 #include "chrome/browser/chrome_notification_types.h" | |
| 16 #include "chrome/browser/profiles/profile.h" | |
| 17 #include "components/ssl_errors/error_info.h" | 14 #include "components/ssl_errors/error_info.h" |
| 18 #include "content/public/browser/notification_service.h" | |
| 19 #include "content/public/browser/web_contents.h" | |
| 20 #include "net/base/net_util.h" | 15 #include "net/base/net_util.h" |
| 21 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 16 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| 22 #include "net/cert/x509_cert_types.h" | 17 #include "net/cert/x509_cert_types.h" |
| 23 #include "net/cert/x509_certificate.h" | 18 #include "net/cert/x509_certificate.h" |
| 24 #include "url/gurl.h" | 19 #include "url/gurl.h" |
| 25 | 20 |
| 26 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
| 27 #include "chrome/browser/captive_portal/captive_portal_service.h" | |
| 28 #include "chrome/browser/captive_portal/captive_portal_service_factory.h" | |
| 29 #endif | |
| 30 | |
| 31 #if defined(OS_WIN) | 21 #if defined(OS_WIN) |
| 32 #include "base/win/win_util.h" | 22 #include "base/win/win_util.h" |
| 33 #include "base/win/windows_version.h" | 23 #include "base/win/windows_version.h" |
| 34 #endif | 24 #endif |
| 35 | 25 |
| 36 using base::Time; | 26 using base::Time; |
| 37 using base::TimeTicks; | 27 using base::TimeTicks; |
| 38 using base::TimeDelta; | 28 using base::TimeDelta; |
| 39 | 29 |
| 40 namespace { | 30 namespace { |
| 41 | 31 |
| 42 // Events for UMA. Do not reorder or change! | 32 // Events for UMA. Do not reorder or change! |
| 43 enum SSLInterstitialCause { | 33 enum SSLInterstitialCause { |
| 44 CLOCK_PAST, | 34 CLOCK_PAST, |
| 45 CLOCK_FUTURE, | 35 CLOCK_FUTURE, |
| 46 WWW_SUBDOMAIN_MATCH, | 36 WWW_SUBDOMAIN_MATCH, |
| 47 SUBDOMAIN_MATCH, | 37 SUBDOMAIN_MATCH, |
| 48 SUBDOMAIN_INVERSE_MATCH, | 38 SUBDOMAIN_INVERSE_MATCH, |
| 49 SUBDOMAIN_OUTSIDE_WILDCARD, | 39 SUBDOMAIN_OUTSIDE_WILDCARD, |
| 50 HOST_NAME_NOT_KNOWN_TLD, | 40 HOST_NAME_NOT_KNOWN_TLD, |
| 51 LIKELY_MULTI_TENANT_HOSTING, | 41 LIKELY_MULTI_TENANT_HOSTING, |
| 52 LOCALHOST, | 42 LOCALHOST, |
| 53 PRIVATE_URL, | 43 PRIVATE_URL, |
| 54 AUTHORITY_ERROR_CAPTIVE_PORTAL, | 44 AUTHORITY_ERROR_CAPTIVE_PORTAL, // Deprecated in M47. |
| 55 SELF_SIGNED, | 45 SELF_SIGNED, |
| 56 EXPIRED_RECENTLY, | 46 EXPIRED_RECENTLY, |
| 57 LIKELY_SAME_DOMAIN, | 47 LIKELY_SAME_DOMAIN, |
| 58 UNUSED_INTERSTITIAL_CAUSE_ENTRY, | 48 UNUSED_INTERSTITIAL_CAUSE_ENTRY, |
| 59 }; | 49 }; |
| 60 | 50 |
| 61 // Events for UMA. Do not reorder or change! | |
| 62 enum SSLInterstitialCauseCaptivePortal { | |
| 63 CAPTIVE_PORTAL_ALL, | |
| 64 CAPTIVE_PORTAL_DETECTION_ENABLED, | |
| 65 CAPTIVE_PORTAL_DETECTION_ENABLED_OVERRIDABLE, | |
| 66 CAPTIVE_PORTAL_PROBE_COMPLETED, | |
| 67 CAPTIVE_PORTAL_PROBE_COMPLETED_OVERRIDABLE, | |
| 68 CAPTIVE_PORTAL_NO_RESPONSE, | |
| 69 CAPTIVE_PORTAL_NO_RESPONSE_OVERRIDABLE, | |
| 70 CAPTIVE_PORTAL_DETECTED, | |
| 71 CAPTIVE_PORTAL_DETECTED_OVERRIDABLE, | |
| 72 UNUSED_CAPTIVE_PORTAL_EVENT, | |
| 73 }; | |
| 74 | |
| 75 void RecordSSLInterstitialCause(bool overridable, SSLInterstitialCause event) { | 51 void RecordSSLInterstitialCause(bool overridable, SSLInterstitialCause event) { |
| 76 if (overridable) { | 52 if (overridable) { |
| 77 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.overridable", event, | 53 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.overridable", event, |
| 78 UNUSED_INTERSTITIAL_CAUSE_ENTRY); | 54 UNUSED_INTERSTITIAL_CAUSE_ENTRY); |
| 79 } else { | 55 } else { |
| 80 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.nonoverridable", event, | 56 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.nonoverridable", event, |
| 81 UNUSED_INTERSTITIAL_CAUSE_ENTRY); | 57 UNUSED_INTERSTITIAL_CAUSE_ENTRY); |
| 82 } | 58 } |
| 83 } | 59 } |
| 84 | 60 |
| 85 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
| 86 void RecordCaptivePortalEventStats(SSLInterstitialCauseCaptivePortal event) { | |
| 87 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.captive_portal", | |
| 88 event, | |
| 89 UNUSED_CAPTIVE_PORTAL_EVENT); | |
| 90 } | |
| 91 #endif | |
| 92 | |
| 93 int GetLevensteinDistance(const std::string& str1, | 61 int GetLevensteinDistance(const std::string& str1, |
| 94 const std::string& str2) { | 62 const std::string& str2) { |
| 95 if (str1 == str2) | 63 if (str1 == str2) |
| 96 return 0; | 64 return 0; |
| 97 if (str1.size() == 0) | 65 if (str1.size() == 0) |
| 98 return str2.size(); | 66 return str2.size(); |
| 99 if (str2.size() == 0) | 67 if (str2.size() == 0) |
| 100 return str1.size(); | 68 return str1.size(); |
| 101 std::vector<int> kFirstRow(str2.size() + 1, 0); | 69 std::vector<int> kFirstRow(str2.size() + 1, 0); |
| 102 std::vector<int> kSecondRow(str2.size() + 1, 0); | 70 std::vector<int> kSecondRow(str2.size() + 1, 0); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 114 kFirstRow[j] = kSecondRow[j]; | 82 kFirstRow[j] = kSecondRow[j]; |
| 115 } | 83 } |
| 116 return kSecondRow[str2.size()]; | 84 return kSecondRow[str2.size()]; |
| 117 } | 85 } |
| 118 | 86 |
| 119 // The time to use when doing build time operations in browser tests. | 87 // The time to use when doing build time operations in browser tests. |
| 120 base::Time g_testing_build_time; | 88 base::Time g_testing_build_time; |
| 121 | 89 |
| 122 } // namespace | 90 } // namespace |
| 123 | 91 |
| 124 SSLErrorClassification::SSLErrorClassification( | 92 SSLErrorClassification::SSLErrorClassification(const base::Time& current_time, |
| 125 content::WebContents* web_contents, | 93 const GURL& url, |
| 126 const base::Time& current_time, | 94 int cert_error, |
| 127 const GURL& url, | 95 const net::X509Certificate& cert) |
| 128 int cert_error, | 96 : current_time_(current_time), |
| 129 const net::X509Certificate& cert) | 97 request_url_(url), |
| 130 : web_contents_(web_contents), | 98 cert_error_(cert_error), |
| 131 current_time_(current_time), | 99 cert_(cert) {} |
| 132 request_url_(url), | |
| 133 cert_error_(cert_error), | |
| 134 cert_(cert), | |
| 135 captive_portal_detection_enabled_(false), | |
| 136 captive_portal_probe_completed_(false), | |
| 137 captive_portal_no_response_(false), | |
| 138 captive_portal_detected_(false) { | |
| 139 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
| 140 Profile* profile = Profile::FromBrowserContext( | |
| 141 web_contents_->GetBrowserContext()); | |
| 142 captive_portal_detection_enabled_ = | |
| 143 CaptivePortalServiceFactory::GetForProfile(profile)->enabled(); | |
| 144 registrar_.Add(this, | |
| 145 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, | |
| 146 content::Source<Profile>(profile)); | |
| 147 #endif | |
| 148 } | |
| 149 | 100 |
| 150 SSLErrorClassification::~SSLErrorClassification() { } | 101 SSLErrorClassification::~SSLErrorClassification() { } |
| 151 | 102 |
| 152 void SSLErrorClassification::RecordCaptivePortalUMAStatistics( | |
| 153 bool overridable) const { | |
| 154 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
| 155 RecordCaptivePortalEventStats(CAPTIVE_PORTAL_ALL); | |
| 156 if (captive_portal_detection_enabled_) | |
| 157 RecordCaptivePortalEventStats( | |
| 158 overridable ? | |
| 159 CAPTIVE_PORTAL_DETECTION_ENABLED_OVERRIDABLE : | |
| 160 CAPTIVE_PORTAL_DETECTION_ENABLED); | |
| 161 if (captive_portal_probe_completed_) | |
| 162 RecordCaptivePortalEventStats( | |
| 163 overridable ? | |
| 164 CAPTIVE_PORTAL_PROBE_COMPLETED_OVERRIDABLE : | |
| 165 CAPTIVE_PORTAL_PROBE_COMPLETED); | |
| 166 // Log only one of portal detected and no response results. | |
| 167 if (captive_portal_detected_) | |
| 168 RecordCaptivePortalEventStats( | |
| 169 overridable ? | |
| 170 CAPTIVE_PORTAL_DETECTED_OVERRIDABLE : | |
| 171 CAPTIVE_PORTAL_DETECTED); | |
| 172 else if (captive_portal_no_response_) | |
| 173 RecordCaptivePortalEventStats( | |
| 174 overridable ? | |
| 175 CAPTIVE_PORTAL_NO_RESPONSE_OVERRIDABLE : | |
| 176 CAPTIVE_PORTAL_NO_RESPONSE); | |
| 177 #endif | |
| 178 } | |
| 179 | |
| 180 void SSLErrorClassification::RecordUMAStatistics( | 103 void SSLErrorClassification::RecordUMAStatistics( |
| 181 bool overridable) const { | 104 bool overridable) const { |
| 182 ssl_errors::ErrorInfo::ErrorType type = | 105 ssl_errors::ErrorInfo::ErrorType type = |
| 183 ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error_); | 106 ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error_); |
| 184 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_type", type, | 107 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_type", type, |
| 185 ssl_errors::ErrorInfo::END_OF_ENUM); | 108 ssl_errors::ErrorInfo::END_OF_ENUM); |
| 186 switch (type) { | 109 switch (type) { |
| 187 case ssl_errors::ErrorInfo::CERT_DATE_INVALID: { | 110 case ssl_errors::ErrorInfo::CERT_DATE_INVALID: { |
| 188 if (IsUserClockInThePast(base::Time::NowFromSystemTime())) { | 111 if (IsUserClockInThePast(base::Time::NowFromSystemTime())) { |
| 189 RecordSSLInterstitialCause(overridable, CLOCK_PAST); | 112 RecordSSLInterstitialCause(overridable, CLOCK_PAST); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 218 RecordSSLInterstitialCause(overridable, HOST_NAME_NOT_KNOWN_TLD); | 141 RecordSSLInterstitialCause(overridable, HOST_NAME_NOT_KNOWN_TLD); |
| 219 } | 142 } |
| 220 break; | 143 break; |
| 221 } | 144 } |
| 222 case ssl_errors::ErrorInfo::CERT_AUTHORITY_INVALID: { | 145 case ssl_errors::ErrorInfo::CERT_AUTHORITY_INVALID: { |
| 223 const std::string& hostname = request_url_.HostNoBrackets(); | 146 const std::string& hostname = request_url_.HostNoBrackets(); |
| 224 if (net::IsLocalhost(hostname)) | 147 if (net::IsLocalhost(hostname)) |
| 225 RecordSSLInterstitialCause(overridable, LOCALHOST); | 148 RecordSSLInterstitialCause(overridable, LOCALHOST); |
| 226 if (IsHostnameNonUniqueOrDotless(hostname)) | 149 if (IsHostnameNonUniqueOrDotless(hostname)) |
| 227 RecordSSLInterstitialCause(overridable, PRIVATE_URL); | 150 RecordSSLInterstitialCause(overridable, PRIVATE_URL); |
| 228 if (captive_portal_probe_completed_ && captive_portal_detected_) | |
| 229 RecordSSLInterstitialCause(overridable, AUTHORITY_ERROR_CAPTIVE_PORTAL); | |
| 230 if (net::X509Certificate::IsSelfSigned(cert_.os_cert_handle())) | 151 if (net::X509Certificate::IsSelfSigned(cert_.os_cert_handle())) |
| 231 RecordSSLInterstitialCause(overridable, SELF_SIGNED); | 152 RecordSSLInterstitialCause(overridable, SELF_SIGNED); |
| 232 break; | 153 break; |
| 233 } | 154 } |
| 234 default: | 155 default: |
| 235 break; | 156 break; |
| 236 } | 157 } |
| 237 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.connection_type", | 158 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.connection_type", |
| 238 net::NetworkChangeNotifier::GetConnectionType(), | 159 net::NetworkChangeNotifier::GetConnectionType(), |
| 239 net::NetworkChangeNotifier::CONNECTION_LAST); | 160 net::NetworkChangeNotifier::CONNECTION_LAST); |
| (...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 514 return std::find(dns_names_domain.begin(), dns_names_domain.end() - 1, | 435 return std::find(dns_names_domain.begin(), dns_names_domain.end() - 1, |
| 515 host_name_domain) != dns_names_domain.end() - 1; | 436 host_name_domain) != dns_names_domain.end() - 1; |
| 516 } | 437 } |
| 517 | 438 |
| 518 // static | 439 // static |
| 519 bool SSLErrorClassification::IsHostnameNonUniqueOrDotless( | 440 bool SSLErrorClassification::IsHostnameNonUniqueOrDotless( |
| 520 const std::string& hostname) { | 441 const std::string& hostname) { |
| 521 return net::IsHostnameNonUnique(hostname) || | 442 return net::IsHostnameNonUnique(hostname) || |
| 522 hostname.find('.') == std::string::npos; | 443 hostname.find('.') == std::string::npos; |
| 523 } | 444 } |
| 524 | |
| 525 void SSLErrorClassification::Observe( | |
| 526 int type, | |
| 527 const content::NotificationSource& source, | |
| 528 const content::NotificationDetails& details) { | |
| 529 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
| 530 // When detection is disabled, captive portal service always sends | |
| 531 // RESULT_INTERNET_CONNECTED. Ignore any probe results in that case. | |
| 532 if (!captive_portal_detection_enabled_) | |
| 533 return; | |
| 534 if (type == chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT) { | |
| 535 captive_portal_probe_completed_ = true; | |
| 536 CaptivePortalService::Results* results = | |
| 537 content::Details<CaptivePortalService::Results>(details).ptr(); | |
| 538 // If a captive portal was detected at any point when the interstitial was | |
| 539 // displayed, assume that the interstitial was caused by a captive portal. | |
| 540 // Example scenario: | |
| 541 // 1- Interstitial displayed and captive portal detected, setting the flag. | |
| 542 // 2- Captive portal detection automatically opens portal login page. | |
| 543 // 3- User logs in on the portal login page. | |
| 544 // A notification will be received here for RESULT_INTERNET_CONNECTED. Make | |
| 545 // sure we don't clear the captive protal flag, since the interstitial was | |
| 546 // potentially caused by the captive portal. | |
| 547 captive_portal_detected_ = captive_portal_detected_ || | |
| 548 (results->result == captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL); | |
| 549 // Also keep track of non-HTTP portals and error cases. | |
| 550 captive_portal_no_response_ = captive_portal_no_response_ || | |
| 551 (results->result == captive_portal::RESULT_NO_RESPONSE); | |
| 552 } | |
| 553 #endif | |
| 554 } | |
| OLD | NEW |