| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 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 |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "chrome/browser/ssl/ssl_error_handler.h" |
| 6 | |
| avi | 664c07b | 2015-12-26 02:18:31 | [diff] [blame] | 7 | #include <stdint.h> |
| dcheng | e73d8520c | 2015-12-27 01:19:09 | [diff] [blame] | 8 | #include <utility> |
| avi | 664c07b | 2015-12-26 02:18:31 | [diff] [blame] | 9 | |
| meacer | d2c91b4 | 2015-03-20 18:34:40 | [diff] [blame] | 10 | #include "base/callback_helpers.h" |
| meacer | 8016685 | 2016-12-08 01:51:36 | [diff] [blame^] | 11 | #include "base/feature_list.h" |
| avi | 664c07b | 2015-12-26 02:18:31 | [diff] [blame] | 12 | #include "base/macros.h" |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 13 | #include "base/metrics/field_trial.h" |
| asvitkine | aa06031 | 2016-09-01 22:44:13 | [diff] [blame] | 14 | #include "base/metrics/histogram_macros.h" |
| bhanudev | d6bd778 | 2015-08-21 00:01:54 | [diff] [blame] | 15 | #include "base/strings/stringprintf.h" |
| felt | 534cc9e7 | 2015-09-01 20:38:59 | [diff] [blame] | 16 | #include "base/time/clock.h" |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 17 | #include "base/time/time.h" |
| mab | 740b02b | 2016-03-17 23:44:21 | [diff] [blame] | 18 | #include "chrome/browser/browser_process.h" |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 19 | #include "chrome/browser/profiles/profile.h" |
| felt | 534cc9e7 | 2015-09-01 20:38:59 | [diff] [blame] | 20 | #include "chrome/browser/ssl/bad_clock_blocking_page.h" |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 21 | #include "chrome/browser/ssl/ssl_blocking_page.h" |
| estark | 3ba1113 | 2015-04-10 01:38:33 | [diff] [blame] | 22 | #include "chrome/browser/ssl/ssl_cert_reporter.h" |
| brettw | ab78fef | 2016-10-12 02:56:05 | [diff] [blame] | 23 | #include "chrome/common/features.h" |
| estark | 5057f82 | 2016-11-08 21:34:54 | [diff] [blame] | 24 | #include "components/network_time/network_time_tracker.h" |
| felt | 70127b4 | 2015-11-10 21:57:23 | [diff] [blame] | 25 | #include "components/ssl_errors/error_classification.h" |
| felt | 2493b445 | 2015-09-17 20:33:59 | [diff] [blame] | 26 | #include "components/ssl_errors/error_info.h" |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 27 | #include "content/public/browser/notification_service.h" |
| 28 | #include "content/public/browser/notification_source.h" |
| bhanudev | d6bd778 | 2015-08-21 00:01:54 | [diff] [blame] | 29 | #include "content/public/browser/render_frame_host.h" |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 30 | #include "content/public/browser/web_contents.h" |
| bhanudev | 3999e37 | 2015-08-21 07:23:10 | [diff] [blame] | 31 | #include "net/base/net_errors.h" |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 32 | |
| brettw | ab78fef | 2016-10-12 02:56:05 | [diff] [blame] | 33 | #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 34 | #include "chrome/browser/captive_portal/captive_portal_service.h" |
| 35 | #include "chrome/browser/captive_portal/captive_portal_service_factory.h" |
| 36 | #include "chrome/browser/captive_portal/captive_portal_tab_helper.h" |
| 37 | #include "chrome/browser/ssl/captive_portal_blocking_page.h" |
| 38 | #endif |
| 39 | |
| mab | 740b02b | 2016-03-17 23:44:21 | [diff] [blame] | 40 | namespace network_time { |
| 41 | class NetworkTimeTracker; |
| 42 | } |
| 43 | |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 44 | namespace { |
| 45 | |
| meacer | 8016685 | 2016-12-08 01:51:36 | [diff] [blame^] | 46 | #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| 47 | const base::Feature kCaptivePortalInterstitial{ |
| 48 | "CaptivePortalInterstitial", base::FEATURE_ENABLED_BY_DEFAULT}; |
| 49 | #endif |
| 50 | |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 51 | // The delay in milliseconds before displaying the SSL interstitial. |
| 52 | // This can be changed in tests. |
| 53 | // - If there is a name mismatch and a suggested URL available result arrives |
| 54 | // during this time, the user is redirected to the suggester URL. |
| 55 | // - If a "captive portal detected" result arrives during this time, |
| 56 | // a captive portal interstitial is displayed. |
| 57 | // - Otherwise, an SSL interstitial is displayed. |
| meacer | f940d5d | 2016-04-05 19:36:38 | [diff] [blame] | 58 | int64_t g_interstitial_delay_in_milliseconds = 3000; |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 59 | |
| 60 | // Callback to call when the interstitial timer is started. Used for testing. |
| 61 | SSLErrorHandler::TimerStartedCallback* g_timer_started_callback = nullptr; |
| 62 | |
| felt | 534cc9e7 | 2015-09-01 20:38:59 | [diff] [blame] | 63 | // The clock to use when deciding which error type to display. Used for testing. |
| 64 | base::Clock* g_testing_clock = nullptr; |
| 65 | |
| estark | 5057f82 | 2016-11-08 21:34:54 | [diff] [blame] | 66 | network_time::NetworkTimeTracker* g_network_time_tracker = nullptr; |
| 67 | |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 68 | // Events for UMA. |
| 69 | enum SSLErrorHandlerEvent { |
| 70 | HANDLE_ALL, |
| 71 | SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE, |
| 72 | SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE, |
| 73 | SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE, |
| 74 | SHOW_SSL_INTERSTITIAL_OVERRIDABLE, |
| bhanudev | 45d9a43 | 2015-08-20 02:38:51 | [diff] [blame] | 75 | WWW_MISMATCH_FOUND, |
| 76 | WWW_MISMATCH_URL_AVAILABLE, |
| 77 | WWW_MISMATCH_URL_NOT_AVAILABLE, |
| felt | 534cc9e7 | 2015-09-01 20:38:59 | [diff] [blame] | 78 | SHOW_BAD_CLOCK, |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 79 | SSL_ERROR_HANDLER_EVENT_COUNT |
| 80 | }; |
| 81 | |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 82 | // Adds a message to console after navigation commits and then, deletes itself. |
| 83 | // Also deletes itself if the navigation is stopped. |
| 84 | class CommonNameMismatchRedirectObserver |
| 85 | : public content::WebContentsObserver, |
| 86 | public content::WebContentsUserData<CommonNameMismatchRedirectObserver> { |
| 87 | public: |
| 88 | static void AddToConsoleAfterNavigation( |
| 89 | content::WebContents* web_contents, |
| 90 | const std::string& request_url_hostname, |
| 91 | const std::string& suggested_url_hostname) { |
| 92 | web_contents->SetUserData( |
| 93 | UserDataKey(), |
| 94 | new CommonNameMismatchRedirectObserver( |
| 95 | web_contents, request_url_hostname, suggested_url_hostname)); |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 96 | } |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 97 | |
| 98 | private: |
| 99 | CommonNameMismatchRedirectObserver(content::WebContents* web_contents, |
| 100 | const std::string& request_url_hostname, |
| 101 | const std::string& suggested_url_hostname) |
| 102 | : WebContentsObserver(web_contents), |
| 103 | web_contents_(web_contents), |
| 104 | request_url_hostname_(request_url_hostname), |
| 105 | suggested_url_hostname_(suggested_url_hostname) {} |
| 106 | ~CommonNameMismatchRedirectObserver() override {} |
| 107 | |
| 108 | // WebContentsObserver: |
| 109 | void NavigationStopped() override { |
| 110 | // Deletes |this|. |
| 111 | web_contents_->RemoveUserData(UserDataKey()); |
| 112 | } |
| 113 | |
| 114 | void NavigationEntryCommitted( |
| 115 | const content::LoadCommittedDetails& /* load_details */) override { |
| 116 | web_contents_->GetMainFrame()->AddMessageToConsole( |
| 117 | content::CONSOLE_MESSAGE_LEVEL_LOG, |
| 118 | base::StringPrintf( |
| 119 | "Redirecting navigation %s -> %s because the server presented a " |
| meacer | ee63a4e | 2016-03-30 23:03:19 | [diff] [blame] | 120 | "certificate valid for %s but not for %s. To disable such " |
| 121 | "redirects launch Chrome with the following flag: " |
| 122 | "--force-fieldtrials=SSLCommonNameMismatchHandling/Disabled/", |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 123 | request_url_hostname_.c_str(), suggested_url_hostname_.c_str(), |
| 124 | suggested_url_hostname_.c_str(), request_url_hostname_.c_str())); |
| 125 | web_contents_->RemoveUserData(UserDataKey()); |
| 126 | } |
| 127 | |
| 128 | void WebContentsDestroyed() override { |
| 129 | web_contents_->RemoveUserData(UserDataKey()); |
| 130 | } |
| 131 | |
| 132 | content::WebContents* web_contents_; |
| 133 | const std::string request_url_hostname_; |
| 134 | const std::string suggested_url_hostname_; |
| 135 | |
| 136 | DISALLOW_COPY_AND_ASSIGN(CommonNameMismatchRedirectObserver); |
| 137 | }; |
| 138 | |
| 139 | void RecordUMA(SSLErrorHandlerEvent event) { |
| 140 | UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_handler", event, |
| 141 | SSL_ERROR_HANDLER_EVENT_COUNT); |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 142 | } |
| meacer | 5c77768 | 2015-01-09 06:54:53 | [diff] [blame] | 143 | |
| brettw | ab78fef | 2016-10-12 02:56:05 | [diff] [blame] | 144 | #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| meacer | 5c77768 | 2015-01-09 06:54:53 | [diff] [blame] | 145 | bool IsCaptivePortalInterstitialEnabled() { |
| meacer | 8016685 | 2016-12-08 01:51:36 | [diff] [blame^] | 146 | return base::FeatureList::IsEnabled(kCaptivePortalInterstitial); |
| meacer | 5c77768 | 2015-01-09 06:54:53 | [diff] [blame] | 147 | } |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 148 | #endif |
| 149 | |
| bhanudev | 4b280dc | 2015-08-20 20:43:00 | [diff] [blame] | 150 | bool IsSSLCommonNameMismatchHandlingEnabled() { |
| 151 | return base::FieldTrialList::FindFullName("SSLCommonNameMismatchHandling") == |
| 152 | "Enabled"; |
| 153 | } |
| 154 | |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 155 | } // namespace |
| 156 | |
| 157 | DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler); |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 158 | DEFINE_WEB_CONTENTS_USER_DATA_KEY(CommonNameMismatchRedirectObserver); |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 159 | |
| 160 | void SSLErrorHandler::HandleSSLError( |
| 161 | content::WebContents* web_contents, |
| 162 | int cert_error, |
| 163 | const net::SSLInfo& ssl_info, |
| 164 | const GURL& request_url, |
| 165 | int options_mask, |
| dcheng | 4af4858 | 2016-04-19 00:29:35 | [diff] [blame] | 166 | std::unique_ptr<SSLCertReporter> ssl_cert_reporter, |
| estark | 719dde5 | 2016-08-09 03:14:27 | [diff] [blame] | 167 | const base::Callback<void(content::CertificateRequestResultType)>& |
| 168 | callback) { |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 169 | DCHECK(!FromWebContents(web_contents)); |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 170 | SSLErrorHandler* error_handler = |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 171 | new SSLErrorHandler(web_contents, cert_error, ssl_info, request_url, |
| dcheng | e73d8520c | 2015-12-27 01:19:09 | [diff] [blame] | 172 | options_mask, std::move(ssl_cert_reporter), callback); |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 173 | web_contents->SetUserData(UserDataKey(), error_handler); |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 174 | error_handler->StartHandlingError(); |
| 175 | } |
| 176 | |
| 177 | // static |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 178 | void SSLErrorHandler::SetInterstitialDelayForTest(base::TimeDelta delay) { |
| 179 | g_interstitial_delay_in_milliseconds = delay.InMilliseconds(); |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | // static |
| 183 | void SSLErrorHandler::SetInterstitialTimerStartedCallbackForTest( |
| 184 | TimerStartedCallback* callback) { |
| 185 | DCHECK(!callback || !callback->is_null()); |
| 186 | g_timer_started_callback = callback; |
| 187 | } |
| 188 | |
| felt | 534cc9e7 | 2015-09-01 20:38:59 | [diff] [blame] | 189 | // static |
| 190 | void SSLErrorHandler::SetClockForTest(base::Clock* testing_clock) { |
| 191 | g_testing_clock = testing_clock; |
| 192 | } |
| 193 | |
| estark | 5057f82 | 2016-11-08 21:34:54 | [diff] [blame] | 194 | // static |
| 195 | void SSLErrorHandler::SetNetworkTimeTrackerForTest( |
| 196 | network_time::NetworkTimeTracker* tracker) { |
| 197 | g_network_time_tracker = tracker; |
| 198 | } |
| 199 | |
| dcheng | 4af4858 | 2016-04-19 00:29:35 | [diff] [blame] | 200 | SSLErrorHandler::SSLErrorHandler( |
| 201 | content::WebContents* web_contents, |
| 202 | int cert_error, |
| 203 | const net::SSLInfo& ssl_info, |
| 204 | const GURL& request_url, |
| 205 | int options_mask, |
| 206 | std::unique_ptr<SSLCertReporter> ssl_cert_reporter, |
| estark | 719dde5 | 2016-08-09 03:14:27 | [diff] [blame] | 207 | const base::Callback<void(content::CertificateRequestResultType)>& callback) |
| meacer | d2c91b4 | 2015-03-20 18:34:40 | [diff] [blame] | 208 | : content::WebContentsObserver(web_contents), |
| 209 | web_contents_(web_contents), |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 210 | cert_error_(cert_error), |
| 211 | ssl_info_(ssl_info), |
| 212 | request_url_(request_url), |
| 213 | options_mask_(options_mask), |
| estark | 93272ab | 2015-03-25 23:54:01 | [diff] [blame] | 214 | callback_(callback), |
| bhanudev | 2051ce7 | 2015-08-18 22:21:18 | [diff] [blame] | 215 | profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())), |
| estark | 5057f82 | 2016-11-08 21:34:54 | [diff] [blame] | 216 | ssl_cert_reporter_(std::move(ssl_cert_reporter)), |
| 217 | weak_ptr_factory_(this) {} |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 218 | |
| 219 | SSLErrorHandler::~SSLErrorHandler() { |
| 220 | } |
| 221 | |
| 222 | void SSLErrorHandler::StartHandlingError() { |
| 223 | RecordUMA(HANDLE_ALL); |
| 224 | |
| mab | 740b02b | 2016-03-17 23:44:21 | [diff] [blame] | 225 | if (ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error_) == |
| 226 | ssl_errors::ErrorInfo::CERT_DATE_INVALID) { |
| estark | 5057f82 | 2016-11-08 21:34:54 | [diff] [blame] | 227 | HandleCertDateInvalidError(); |
| 228 | return; |
| felt | 534cc9e7 | 2015-09-01 20:38:59 | [diff] [blame] | 229 | } |
| 230 | |
| bhanudev | 2051ce7 | 2015-08-18 22:21:18 | [diff] [blame] | 231 | std::vector<std::string> dns_names; |
| 232 | ssl_info_.cert->GetDNSNames(&dns_names); |
| 233 | DCHECK(!dns_names.empty()); |
| 234 | GURL suggested_url; |
| bhanudev | 4b280dc | 2015-08-20 20:43:00 | [diff] [blame] | 235 | if (IsSSLCommonNameMismatchHandlingEnabled() && |
| bhanudev | 3999e37 | 2015-08-21 07:23:10 | [diff] [blame] | 236 | cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID && |
| 237 | IsErrorOverridable() && GetSuggestedUrl(dns_names, &suggested_url)) { |
| bhanudev | 45d9a43 | 2015-08-20 02:38:51 | [diff] [blame] | 238 | RecordUMA(WWW_MISMATCH_FOUND); |
| bhanudev | 2051ce7 | 2015-08-18 22:21:18 | [diff] [blame] | 239 | net::CertStatus extra_cert_errors = |
| 240 | ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID; |
| 241 | |
| 242 | // Show the SSL intersitial if |CERT_STATUS_COMMON_NAME_INVALID| is not |
| 243 | // the only error. Need not check for captive portal in this case. |
| 244 | // (See the comment below). |
| 245 | if (net::IsCertStatusError(extra_cert_errors) && |
| 246 | !net::IsCertStatusMinorError(ssl_info_.cert_status)) { |
| 247 | ShowSSLInterstitial(); |
| 248 | return; |
| 249 | } |
| 250 | CheckSuggestedUrl(suggested_url); |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 251 | timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds( |
| 252 | g_interstitial_delay_in_milliseconds), |
| 253 | this, &SSLErrorHandler::ShowSSLInterstitial); |
| bhanudev | 2051ce7 | 2015-08-18 22:21:18 | [diff] [blame] | 254 | if (g_timer_started_callback) |
| 255 | g_timer_started_callback->Run(web_contents_); |
| 256 | |
| 257 | // Do not check for a captive portal in this case, because a captive |
| 258 | // portal most likely cannot serve a valid certificate which passes the |
| 259 | // similarity check. |
| 260 | return; |
| 261 | } |
| 262 | |
| brettw | ab78fef | 2016-10-12 02:56:05 | [diff] [blame] | 263 | #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| bhanudev | 2051ce7 | 2015-08-18 22:21:18 | [diff] [blame] | 264 | CaptivePortalTabHelper* captive_portal_tab_helper = |
| 265 | CaptivePortalTabHelper::FromWebContents(web_contents_); |
| 266 | if (captive_portal_tab_helper) { |
| 267 | captive_portal_tab_helper->OnSSLCertError(ssl_info_); |
| 268 | } |
| 269 | |
| 270 | registrar_.Add(this, chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, |
| 271 | content::Source<Profile>(profile_)); |
| 272 | |
| meacer | 5c77768 | 2015-01-09 06:54:53 | [diff] [blame] | 273 | if (IsCaptivePortalInterstitialEnabled()) { |
| 274 | CheckForCaptivePortal(); |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 275 | timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds( |
| 276 | g_interstitial_delay_in_milliseconds), |
| 277 | this, &SSLErrorHandler::ShowSSLInterstitial); |
| meacer | 5c77768 | 2015-01-09 06:54:53 | [diff] [blame] | 278 | if (g_timer_started_callback) |
| 279 | g_timer_started_callback->Run(web_contents_); |
| 280 | return; |
| 281 | } |
| 282 | #endif |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 283 | // Display an SSL interstitial. |
| 284 | ShowSSLInterstitial(); |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 285 | } |
| 286 | |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 287 | void SSLErrorHandler::CheckForCaptivePortal() { |
| brettw | ab78fef | 2016-10-12 02:56:05 | [diff] [blame] | 288 | #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 289 | CaptivePortalService* captive_portal_service = |
| 290 | CaptivePortalServiceFactory::GetForProfile(profile_); |
| 291 | captive_portal_service->DetectCaptivePortal(); |
| 292 | #else |
| 293 | NOTREACHED(); |
| 294 | #endif |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 295 | } |
| 296 | |
| bhanudev | 2051ce7 | 2015-08-18 22:21:18 | [diff] [blame] | 297 | bool SSLErrorHandler::GetSuggestedUrl(const std::vector<std::string>& dns_names, |
| 298 | GURL* suggested_url) const { |
| 299 | return CommonNameMismatchHandler::GetSuggestedUrl(request_url_, dns_names, |
| 300 | suggested_url); |
| 301 | } |
| 302 | |
| 303 | void SSLErrorHandler::CheckSuggestedUrl(const GURL& suggested_url) { |
| 304 | scoped_refptr<net::URLRequestContextGetter> request_context( |
| 305 | profile_->GetRequestContext()); |
| 306 | common_name_mismatch_handler_.reset( |
| 307 | new CommonNameMismatchHandler(request_url_, request_context)); |
| 308 | |
| 309 | common_name_mismatch_handler_->CheckSuggestedUrl( |
| 310 | suggested_url, |
| 311 | base::Bind(&SSLErrorHandler::CommonNameMismatchHandlerCallback, |
| 312 | base::Unretained(this))); |
| 313 | } |
| 314 | |
| 315 | void SSLErrorHandler::NavigateToSuggestedURL(const GURL& suggested_url) { |
| 316 | content::NavigationController::LoadURLParams load_params(suggested_url); |
| 317 | load_params.transition_type = ui::PAGE_TRANSITION_TYPED; |
| 318 | web_contents()->GetController().LoadURLWithParams(load_params); |
| 319 | } |
| 320 | |
| bhanudev | 3999e37 | 2015-08-21 07:23:10 | [diff] [blame] | 321 | bool SSLErrorHandler::IsErrorOverridable() const { |
| 322 | return SSLBlockingPage::IsOverridable(options_mask_, profile_); |
| 323 | } |
| 324 | |
| meacer | ca64d9819 | 2015-01-23 21:34:30 | [diff] [blame] | 325 | void SSLErrorHandler::ShowCaptivePortalInterstitial(const GURL& landing_url) { |
| brettw | ab78fef | 2016-10-12 02:56:05 | [diff] [blame] | 326 | #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 327 | // Show captive portal blocking page. The interstitial owns the blocking page. |
| bhanudev | 3999e37 | 2015-08-21 07:23:10 | [diff] [blame] | 328 | RecordUMA(IsErrorOverridable() |
| fahl | e66e7ff | 2015-04-23 19:38:30 | [diff] [blame] | 329 | ? SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE |
| 330 | : SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE); |
| meacer | ca64d9819 | 2015-01-23 21:34:30 | [diff] [blame] | 331 | (new CaptivePortalBlockingPage(web_contents_, request_url_, landing_url, |
| dcheng | e73d8520c | 2015-12-27 01:19:09 | [diff] [blame] | 332 | std::move(ssl_cert_reporter_), ssl_info_, |
| 333 | callback_)) |
| 334 | ->Show(); |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 335 | // Once an interstitial is displayed, no need to keep the handler around. |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 336 | // This is the equivalent of "delete this". It also destroys the timer. |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 337 | web_contents_->RemoveUserData(UserDataKey()); |
| 338 | #else |
| 339 | NOTREACHED(); |
| 340 | #endif |
| 341 | } |
| 342 | |
| 343 | void SSLErrorHandler::ShowSSLInterstitial() { |
| 344 | // Show SSL blocking page. The interstitial owns the blocking page. |
| bhanudev | 3999e37 | 2015-08-21 07:23:10 | [diff] [blame] | 345 | RecordUMA(IsErrorOverridable() ? SHOW_SSL_INTERSTITIAL_OVERRIDABLE |
| 346 | : SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE); |
| bhanudev | 2051ce7 | 2015-08-18 22:21:18 | [diff] [blame] | 347 | |
| meacer | d19deef | 2016-09-29 02:08:16 | [diff] [blame] | 348 | (SSLBlockingPage::Create(web_contents_, cert_error_, ssl_info_, request_url_, |
| 349 | options_mask_, base::Time::NowFromSystemTime(), |
| 350 | std::move(ssl_cert_reporter_), callback_)) |
| bhanudev | 2051ce7 | 2015-08-18 22:21:18 | [diff] [blame] | 351 | ->Show(); |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 352 | // Once an interstitial is displayed, no need to keep the handler around. |
| 353 | // This is the equivalent of "delete this". |
| 354 | web_contents_->RemoveUserData(UserDataKey()); |
| 355 | } |
| 356 | |
| mab | 740b02b | 2016-03-17 23:44:21 | [diff] [blame] | 357 | void SSLErrorHandler::ShowBadClockInterstitial( |
| 358 | const base::Time& now, |
| 359 | ssl_errors::ClockState clock_state) { |
| felt | 534cc9e7 | 2015-09-01 20:38:59 | [diff] [blame] | 360 | RecordUMA(SHOW_BAD_CLOCK); |
| 361 | (new BadClockBlockingPage(web_contents_, cert_error_, ssl_info_, request_url_, |
| mab | 740b02b | 2016-03-17 23:44:21 | [diff] [blame] | 362 | now, clock_state, std::move(ssl_cert_reporter_), |
| 363 | callback_)) |
| felt | 534cc9e7 | 2015-09-01 20:38:59 | [diff] [blame] | 364 | ->Show(); |
| 365 | // Once an interstitial is displayed, no need to keep the handler around. |
| 366 | // This is the equivalent of "delete this". |
| 367 | web_contents_->RemoveUserData(UserDataKey()); |
| 368 | } |
| 369 | |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 370 | void SSLErrorHandler::CommonNameMismatchHandlerCallback( |
| 371 | const CommonNameMismatchHandler::SuggestedUrlCheckResult& result, |
| 372 | const GURL& suggested_url) { |
| 373 | timer_.Stop(); |
| 374 | if (result == CommonNameMismatchHandler::SuggestedUrlCheckResult:: |
| 375 | SUGGESTED_URL_AVAILABLE) { |
| 376 | RecordUMA(WWW_MISMATCH_URL_AVAILABLE); |
| 377 | CommonNameMismatchRedirectObserver::AddToConsoleAfterNavigation( |
| 378 | web_contents(), request_url_.host(), suggested_url.host()); |
| 379 | NavigateToSuggestedURL(suggested_url); |
| 380 | } else { |
| 381 | RecordUMA(WWW_MISMATCH_URL_NOT_AVAILABLE); |
| 382 | ShowSSLInterstitial(); |
| 383 | } |
| 384 | } |
| 385 | |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 386 | void SSLErrorHandler::Observe( |
| 387 | int type, |
| 388 | const content::NotificationSource& source, |
| 389 | const content::NotificationDetails& details) { |
| brettw | ab78fef | 2016-10-12 02:56:05 | [diff] [blame] | 390 | #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| thestig | 5283579 | 2016-06-13 21:08:09 | [diff] [blame] | 391 | DCHECK_EQ(chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, type); |
| 392 | |
| 393 | timer_.Stop(); |
| 394 | CaptivePortalService::Results* results = |
| 395 | content::Details<CaptivePortalService::Results>(details).ptr(); |
| 396 | if (results->result == captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL) |
| 397 | ShowCaptivePortalInterstitial(results->landing_url); |
| 398 | else |
| 399 | ShowSSLInterstitial(); |
| meacer | 4ef065e | 2015-01-09 03:21:35 | [diff] [blame] | 400 | #endif |
| 401 | } |
| meacer | d2c91b4 | 2015-03-20 18:34:40 | [diff] [blame] | 402 | |
| bhanudev | 85c5b8b5 | 2015-07-31 02:00:37 | [diff] [blame] | 403 | void SSLErrorHandler::DidStartNavigationToPendingEntry( |
| 404 | const GURL& /* url */, |
| toyoshim | 0df1d3a | 2016-09-09 09:52:48 | [diff] [blame] | 405 | content::ReloadType /* reload_type */) { |
| 406 | // Destroy the error handler on all new navigations. This ensures that the |
| 407 | // handler is properly recreated when a hanging page is navigated to an SSL |
| 408 | // error, even when the tab's WebContents doesn't change. |
| bhanudev | 85c5b8b5 | 2015-07-31 02:00:37 | [diff] [blame] | 409 | DeleteSSLErrorHandler(); |
| 410 | } |
| 411 | |
| 412 | void SSLErrorHandler::NavigationStopped() { |
| 413 | // Destroy the error handler when the page load is stopped. |
| 414 | DeleteSSLErrorHandler(); |
| 415 | } |
| 416 | |
| 417 | void SSLErrorHandler::DeleteSSLErrorHandler() { |
| meacer | d2c91b4 | 2015-03-20 18:34:40 | [diff] [blame] | 418 | // Need to explicity deny the certificate via the callback, otherwise memory |
| 419 | // is leaked. |
| 420 | if (!callback_.is_null()) { |
| estark | 719dde5 | 2016-08-09 03:14:27 | [diff] [blame] | 421 | base::ResetAndReturn(&callback_) |
| 422 | .Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); |
| meacer | d2c91b4 | 2015-03-20 18:34:40 | [diff] [blame] | 423 | } |
| bhanudev | 2051ce7 | 2015-08-18 22:21:18 | [diff] [blame] | 424 | if (common_name_mismatch_handler_) { |
| 425 | common_name_mismatch_handler_->Cancel(); |
| 426 | common_name_mismatch_handler_.reset(); |
| 427 | } |
| meacer | 8b4cea2 | 2015-08-28 02:02:55 | [diff] [blame] | 428 | // Deletes |this| and also destroys the timer. |
| meacer | d2c91b4 | 2015-03-20 18:34:40 | [diff] [blame] | 429 | web_contents_->RemoveUserData(UserDataKey()); |
| 430 | } |
| estark | 5057f82 | 2016-11-08 21:34:54 | [diff] [blame] | 431 | |
| 432 | void SSLErrorHandler::HandleCertDateInvalidError() { |
| 433 | network_time::NetworkTimeTracker* tracker = |
| 434 | g_network_time_tracker ? g_network_time_tracker |
| 435 | : g_browser_process->network_time_tracker(); |
| 436 | timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds( |
| 437 | g_interstitial_delay_in_milliseconds), |
| 438 | base::Bind(&SSLErrorHandler::HandleCertDateInvalidErrorImpl, |
| 439 | base::Unretained(this))); |
| 440 | // Try kicking off a time fetch to get an up-to-date estimate of the |
| 441 | // true time. This will only have an effect if network time is |
| 442 | // unavailable or if there is not already a query in progress. |
| 443 | // |
| 444 | // Pass a weak pointer as the callback; if the timer fires before the |
| 445 | // fetch completes and shows an interstitial, this SSLErrorHandler |
| 446 | // will be deleted. |
| 447 | if (!tracker->StartTimeFetch( |
| 448 | base::Bind(&SSLErrorHandler::HandleCertDateInvalidErrorImpl, |
| 449 | weak_ptr_factory_.GetWeakPtr()))) { |
| 450 | HandleCertDateInvalidErrorImpl(); |
| 451 | return; |
| 452 | } |
| 453 | |
| 454 | if (g_timer_started_callback) |
| 455 | g_timer_started_callback->Run(web_contents_); |
| 456 | } |
| 457 | |
| 458 | void SSLErrorHandler::HandleCertDateInvalidErrorImpl() { |
| 459 | network_time::NetworkTimeTracker* tracker = |
| 460 | g_network_time_tracker ? g_network_time_tracker |
| 461 | : g_browser_process->network_time_tracker(); |
| 462 | timer_.Stop(); |
| 463 | const base::Time now = g_testing_clock == nullptr |
| 464 | ? base::Time::NowFromSystemTime() |
| 465 | : g_testing_clock->Now(); |
| 466 | ssl_errors::ClockState clock_state = ssl_errors::GetClockState(now, tracker); |
| 467 | if (clock_state == ssl_errors::CLOCK_STATE_FUTURE || |
| 468 | clock_state == ssl_errors::CLOCK_STATE_PAST) { |
| 469 | ShowBadClockInterstitial(now, clock_state); |
| 470 | return; // |this| is deleted after showing the interstitial. |
| 471 | } |
| 472 | ShowSSLInterstitial(); |
| 473 | } |