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 "chrome/browser/ssl/ssl_error_handler.h" | 5 #include "chrome/browser/ssl/ssl_error_handler.h" |
6 | 6 |
7 #include "base/callback_helpers.h" | 7 #include "base/callback_helpers.h" |
8 #include "base/metrics/field_trial.h" | 8 #include "base/metrics/field_trial.h" |
9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
10 #include "base/time/time.h" | 10 #include "base/time/time.h" |
11 #include "chrome/browser/profiles/profile.h" | 11 #include "chrome/browser/profiles/profile.h" |
12 #include "chrome/browser/ssl/ssl_blocking_page.h" | 12 #include "chrome/browser/ssl/ssl_blocking_page.h" |
13 #include "chrome/browser/ssl/ssl_cert_reporter.h" | 13 #include "chrome/browser/ssl/ssl_cert_reporter.h" |
| 14 #include "chrome/browser/ssl/ssl_error_classification.h" |
14 #include "content/public/browser/notification_service.h" | 15 #include "content/public/browser/notification_service.h" |
15 #include "content/public/browser/notification_source.h" | 16 #include "content/public/browser/notification_source.h" |
16 #include "content/public/browser/web_contents.h" | 17 #include "content/public/browser/web_contents.h" |
17 | 18 |
18 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | 19 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) |
19 #include "chrome/browser/captive_portal/captive_portal_service.h" | 20 #include "chrome/browser/captive_portal/captive_portal_service.h" |
20 #include "chrome/browser/captive_portal/captive_portal_service_factory.h" | 21 #include "chrome/browser/captive_portal/captive_portal_service_factory.h" |
21 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h" | 22 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h" |
22 #include "chrome/browser/ssl/captive_portal_blocking_page.h" | 23 #include "chrome/browser/ssl/captive_portal_blocking_page.h" |
23 #endif | 24 #endif |
(...skipping 17 matching lines...) Expand all Loading... |
41 SHOW_SSL_INTERSTITIAL_OVERRIDABLE, | 42 SHOW_SSL_INTERSTITIAL_OVERRIDABLE, |
42 SSL_ERROR_HANDLER_EVENT_COUNT | 43 SSL_ERROR_HANDLER_EVENT_COUNT |
43 }; | 44 }; |
44 | 45 |
45 void RecordUMA(SSLErrorHandlerEvent event) { | 46 void RecordUMA(SSLErrorHandlerEvent event) { |
46 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_handler", | 47 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_handler", |
47 event, | 48 event, |
48 SSL_ERROR_HANDLER_EVENT_COUNT); | 49 SSL_ERROR_HANDLER_EVENT_COUNT); |
49 } | 50 } |
50 | 51 |
51 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
52 // The delay before displaying the SSL interstitial for cert errors. | 52 // The delay before displaying the SSL interstitial for cert errors. |
53 // - If a "captive portal detected" result arrives in this many seconds, | 53 // - If a "captive portal detected" or "suggested url valid" result |
54 // a captive portal interstitial is displayed. | 54 // arrives in this many seconds, then a captive portal interstitial |
| 55 // or a common name mismatch interstitial is displayed. |
55 // - Otherwise, an SSL interstitial is displayed. | 56 // - Otherwise, an SSL interstitial is displayed. |
56 const int kDefaultInterstitialDisplayDelayInSeconds = 2; | 57 const int kDefaultInterstitialDisplayDelayInSeconds = 2; |
57 | 58 |
58 base::TimeDelta GetInterstitialDisplayDelay( | 59 base::TimeDelta GetInterstitialDisplayDelay( |
59 SSLErrorHandler::InterstitialDelayType delay) { | 60 SSLErrorHandler::InterstitialDelayType delay) { |
60 switch (delay) { | 61 switch (delay) { |
61 case SSLErrorHandler::LONG: | 62 case SSLErrorHandler::LONG: |
62 return base::TimeDelta::FromHours(1); | 63 return base::TimeDelta::FromHours(1); |
63 | 64 |
64 case SSLErrorHandler::NONE: | 65 case SSLErrorHandler::NONE: |
65 return base::TimeDelta(); | 66 return base::TimeDelta(); |
66 | 67 |
67 case SSLErrorHandler::NORMAL: | 68 case SSLErrorHandler::NORMAL: |
68 return base::TimeDelta::FromSeconds( | 69 return base::TimeDelta::FromSeconds( |
69 kDefaultInterstitialDisplayDelayInSeconds); | 70 kDefaultInterstitialDisplayDelayInSeconds); |
70 | 71 |
71 default: | 72 default: |
72 NOTREACHED(); | 73 NOTREACHED(); |
73 } | 74 } |
74 return base::TimeDelta(); | 75 return base::TimeDelta(); |
75 } | 76 } |
76 | 77 |
| 78 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) |
77 bool IsCaptivePortalInterstitialEnabled() { | 79 bool IsCaptivePortalInterstitialEnabled() { |
78 return base::FieldTrialList::FindFullName("CaptivePortalInterstitial") == | 80 return base::FieldTrialList::FindFullName("CaptivePortalInterstitial") == |
79 "Enabled"; | 81 "Enabled"; |
80 } | 82 } |
81 #endif | 83 #endif |
82 | 84 |
83 } // namespace | 85 } // namespace |
84 | 86 |
85 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler); | 87 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler); |
86 | 88 |
87 void SSLErrorHandler::HandleSSLError( | 89 void SSLErrorHandler::HandleSSLError( |
88 content::WebContents* web_contents, | 90 content::WebContents* web_contents, |
89 int cert_error, | 91 int cert_error, |
90 const net::SSLInfo& ssl_info, | 92 const net::SSLInfo& ssl_info, |
91 const GURL& request_url, | 93 const GURL& request_url, |
92 int options_mask, | 94 int options_mask, |
93 scoped_ptr<SSLCertReporter> ssl_cert_reporter, | 95 scoped_ptr<SSLCertReporter> ssl_cert_reporter, |
94 const base::Callback<void(bool)>& callback) { | 96 const base::Callback<void(bool)>& callback) { |
95 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
96 CaptivePortalTabHelper* captive_portal_tab_helper = | |
97 CaptivePortalTabHelper::FromWebContents(web_contents); | |
98 if (captive_portal_tab_helper) { | |
99 captive_portal_tab_helper->OnSSLCertError(ssl_info); | |
100 } | |
101 #endif | |
102 DCHECK(!FromWebContents(web_contents)); | 97 DCHECK(!FromWebContents(web_contents)); |
103 web_contents->SetUserData( | 98 web_contents->SetUserData( |
104 UserDataKey(), | 99 UserDataKey(), |
105 new SSLErrorHandler(web_contents, cert_error, ssl_info, request_url, | 100 new SSLErrorHandler(web_contents, cert_error, ssl_info, request_url, |
106 options_mask, ssl_cert_reporter.Pass(), callback)); | 101 options_mask, ssl_cert_reporter.Pass(), callback)); |
107 | 102 |
108 SSLErrorHandler* error_handler = | 103 SSLErrorHandler* error_handler = |
109 SSLErrorHandler::FromWebContents(web_contents); | 104 SSLErrorHandler::FromWebContents(web_contents); |
110 error_handler->StartHandlingError(); | 105 error_handler->StartHandlingError(); |
111 } | 106 } |
(...skipping 19 matching lines...) Expand all Loading... |
131 scoped_ptr<SSLCertReporter> ssl_cert_reporter, | 126 scoped_ptr<SSLCertReporter> ssl_cert_reporter, |
132 const base::Callback<void(bool)>& callback) | 127 const base::Callback<void(bool)>& callback) |
133 : content::WebContentsObserver(web_contents), | 128 : content::WebContentsObserver(web_contents), |
134 web_contents_(web_contents), | 129 web_contents_(web_contents), |
135 cert_error_(cert_error), | 130 cert_error_(cert_error), |
136 ssl_info_(ssl_info), | 131 ssl_info_(ssl_info), |
137 request_url_(request_url), | 132 request_url_(request_url), |
138 options_mask_(options_mask), | 133 options_mask_(options_mask), |
139 callback_(callback), | 134 callback_(callback), |
140 ssl_cert_reporter_(ssl_cert_reporter.Pass()) { | 135 ssl_cert_reporter_(ssl_cert_reporter.Pass()) { |
141 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
142 Profile* profile = Profile::FromBrowserContext( | |
143 web_contents->GetBrowserContext()); | |
144 registrar_.Add(this, | |
145 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, | |
146 content::Source<Profile>(profile)); | |
147 #endif | |
148 } | 136 } |
149 | 137 |
150 SSLErrorHandler::~SSLErrorHandler() { | 138 SSLErrorHandler::~SSLErrorHandler() { |
151 } | 139 } |
152 | 140 |
153 void SSLErrorHandler::StartHandlingError() { | 141 void SSLErrorHandler::StartHandlingError() { |
154 RecordUMA(HANDLE_ALL); | 142 RecordUMA(HANDLE_ALL); |
155 | 143 |
| 144 std::vector<std::string> dns_names; |
| 145 ssl_info_.cert->GetDNSNames(&dns_names); |
| 146 DCHECK(!dns_names.empty()); |
| 147 GURL suggested_url; |
| 148 if (GetSuggestedUrl(request_url_, dns_names, &suggested_url)) { |
| 149 net::CertStatus extra_cert_errors = |
| 150 ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID; |
| 151 |
| 152 // Show the SSL intersitial if |CERT_STATUS_COMMON_NAME_INVALID| is not |
| 153 // the only error. Need not check for captive portal in this case. |
| 154 // (See the comment below). |
| 155 if (extra_cert_errors) { |
| 156 ShowSSLInterstitial(GURL()); |
| 157 return; |
| 158 } |
| 159 CheckSuggestedUrl(suggested_url); |
| 160 timer_.Start(FROM_HERE, |
| 161 GetInterstitialDisplayDelay(g_interstitial_delay_type), this, |
| 162 &SSLErrorHandler::OnTimerExpired); |
| 163 if (g_timer_started_callback) |
| 164 g_timer_started_callback->Run(web_contents_); |
| 165 |
| 166 // Do not check for a captive portal in this case, because a captive |
| 167 // portal cannot serve a valid certificate which passes the |
| 168 // similarity check. |
| 169 return; |
| 170 } |
| 171 |
156 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | 172 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| 173 CaptivePortalTabHelper* captive_portal_tab_helper = |
| 174 CaptivePortalTabHelper::FromWebContents(web_contents_); |
| 175 if (captive_portal_tab_helper) { |
| 176 captive_portal_tab_helper->OnSSLCertError(ssl_info_); |
| 177 } |
| 178 |
| 179 Profile* profile = |
| 180 Profile::FromBrowserContext(web_contents_->GetBrowserContext()); |
| 181 registrar_.Add(this, chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, |
| 182 content::Source<Profile>(profile)); |
| 183 |
157 if (IsCaptivePortalInterstitialEnabled()) { | 184 if (IsCaptivePortalInterstitialEnabled()) { |
158 CheckForCaptivePortal(); | 185 CheckForCaptivePortal(); |
159 timer_.Start(FROM_HERE, | 186 timer_.Start(FROM_HERE, |
160 GetInterstitialDisplayDelay(g_interstitial_delay_type), | 187 GetInterstitialDisplayDelay(g_interstitial_delay_type), |
161 this, &SSLErrorHandler::OnTimerExpired); | 188 this, &SSLErrorHandler::OnTimerExpired); |
162 if (g_timer_started_callback) | 189 if (g_timer_started_callback) |
163 g_timer_started_callback->Run(web_contents_); | 190 g_timer_started_callback->Run(web_contents_); |
164 return; | 191 return; |
165 } | 192 } |
166 #endif | 193 #endif |
167 // Display an SSL interstitial. | 194 // Display an SSL interstitial. |
168 ShowSSLInterstitial(); | 195 ShowSSLInterstitial(GURL()); |
169 } | 196 } |
170 | 197 |
171 void SSLErrorHandler::OnTimerExpired() { | 198 void SSLErrorHandler::OnTimerExpired() { |
172 ShowSSLInterstitial(); | 199 ShowSSLInterstitial(GURL()); |
| 200 } |
| 201 |
| 202 bool SSLErrorHandler::GetSuggestedUrl(const GURL& request_url, |
| 203 const std::vector<std::string>& dns_names, |
| 204 GURL* suggested_url) { |
| 205 return CommonNameMismatchHandler::GetSuggestedUrl(request_url_, dns_names, |
| 206 suggested_url); |
| 207 } |
| 208 |
| 209 void SSLErrorHandler::CheckSuggestedUrl(const GURL& suggested_url) { |
| 210 Profile* profile = |
| 211 Profile::FromBrowserContext(web_contents_->GetBrowserContext()); |
| 212 scoped_refptr<net::URLRequestContextGetter> request_context( |
| 213 profile->GetRequestContext()); |
| 214 common_name_mismatch_handler_.reset( |
| 215 new CommonNameMismatchHandler(request_url_, request_context)); |
| 216 |
| 217 CommonNameMismatchHandler::testing_state_ = |
| 218 CommonNameMismatchHandler::TestingState::NOT_TESTING; |
| 219 |
| 220 common_name_mismatch_handler_->CheckSuggestedUrl( |
| 221 suggested_url, |
| 222 base::Bind(&SSLErrorHandler::CommonNameMismatchHandlerCallback, |
| 223 base::Unretained(this))); |
173 } | 224 } |
174 | 225 |
175 void SSLErrorHandler::CheckForCaptivePortal() { | 226 void SSLErrorHandler::CheckForCaptivePortal() { |
176 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | 227 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) |
177 Profile* profile = Profile::FromBrowserContext( | 228 Profile* profile = Profile::FromBrowserContext( |
178 web_contents_->GetBrowserContext()); | 229 web_contents_->GetBrowserContext()); |
179 CaptivePortalService* captive_portal_service = | 230 CaptivePortalService* captive_portal_service = |
180 CaptivePortalServiceFactory::GetForProfile(profile); | 231 CaptivePortalServiceFactory::GetForProfile(profile); |
181 captive_portal_service->DetectCaptivePortal(); | 232 captive_portal_service->DetectCaptivePortal(); |
182 #else | 233 #else |
(...skipping 13 matching lines...) Expand all Loading... |
196 ssl_cert_reporter_.Pass(), ssl_info_, | 247 ssl_cert_reporter_.Pass(), ssl_info_, |
197 callback_))->Show(); | 248 callback_))->Show(); |
198 // Once an interstitial is displayed, no need to keep the handler around. | 249 // Once an interstitial is displayed, no need to keep the handler around. |
199 // This is the equivalent of "delete this". | 250 // This is the equivalent of "delete this". |
200 web_contents_->RemoveUserData(UserDataKey()); | 251 web_contents_->RemoveUserData(UserDataKey()); |
201 #else | 252 #else |
202 NOTREACHED(); | 253 NOTREACHED(); |
203 #endif | 254 #endif |
204 } | 255 } |
205 | 256 |
206 void SSLErrorHandler::ShowSSLInterstitial() { | 257 void SSLErrorHandler::ShowSSLInterstitial(const GURL& suggested_url) { |
207 // Show SSL blocking page. The interstitial owns the blocking page. | 258 // Show SSL blocking page. The interstitial owns the blocking page. |
208 const Profile* const profile = | 259 const Profile* const profile = |
209 Profile::FromBrowserContext(web_contents_->GetBrowserContext()); | 260 Profile::FromBrowserContext(web_contents_->GetBrowserContext()); |
210 RecordUMA(SSLBlockingPage::IsOverridable(options_mask_, profile) | 261 RecordUMA(SSLBlockingPage::IsOverridable(options_mask_, profile) |
211 ? SHOW_SSL_INTERSTITIAL_OVERRIDABLE | 262 ? SHOW_SSL_INTERSTITIAL_OVERRIDABLE |
212 : SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE); | 263 : SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE); |
213 (new SSLBlockingPage(web_contents_, cert_error_, ssl_info_, request_url_, | 264 (new SSLBlockingPage(web_contents_, cert_error_, ssl_info_, request_url_, |
214 options_mask_, base::Time::NowFromSystemTime(), | 265 options_mask_, base::Time::NowFromSystemTime(), |
215 ssl_cert_reporter_.Pass(), callback_))->Show(); | 266 ssl_cert_reporter_.Pass(), callback_, suggested_url)) |
| 267 ->Show(); |
216 // Once an interstitial is displayed, no need to keep the handler around. | 268 // Once an interstitial is displayed, no need to keep the handler around. |
217 // This is the equivalent of "delete this". | 269 // This is the equivalent of "delete this". |
218 web_contents_->RemoveUserData(UserDataKey()); | 270 web_contents_->RemoveUserData(UserDataKey()); |
219 } | 271 } |
220 | 272 |
221 void SSLErrorHandler::Observe( | 273 void SSLErrorHandler::Observe( |
222 int type, | 274 int type, |
223 const content::NotificationSource& source, | 275 const content::NotificationSource& source, |
224 const content::NotificationDetails& details) { | 276 const content::NotificationDetails& details) { |
225 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | 277 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) |
226 if (type == chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT) { | 278 if (type == chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT) { |
227 timer_.Stop(); | 279 timer_.Stop(); |
228 CaptivePortalService::Results* results = | 280 CaptivePortalService::Results* results = |
229 content::Details<CaptivePortalService::Results>(details).ptr(); | 281 content::Details<CaptivePortalService::Results>(details).ptr(); |
230 if (results->result == captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL) | 282 if (results->result == captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL) |
231 ShowCaptivePortalInterstitial(results->landing_url); | 283 ShowCaptivePortalInterstitial(results->landing_url); |
232 else | 284 else |
233 ShowSSLInterstitial(); | 285 ShowSSLInterstitial(GURL()); |
234 } | 286 } |
235 #endif | 287 #endif |
236 } | 288 } |
237 | 289 |
238 // Destroy the error handler on all new navigations. This ensures that the | 290 // Destroy the error handler on all new navigations. This ensures that the |
239 // handler is properly recreated when a hanging page is navigated to an SSL | 291 // handler is properly recreated when a hanging page is navigated to an SSL |
240 // error, even when the tab's WebContents doesn't change. | 292 // error, even when the tab's WebContents doesn't change. |
241 void SSLErrorHandler::DidStartNavigationToPendingEntry( | 293 void SSLErrorHandler::DidStartNavigationToPendingEntry( |
242 const GURL& url, | 294 const GURL& url, |
243 content::NavigationController::ReloadType reload_type) { | 295 content::NavigationController::ReloadType reload_type) { |
244 // Need to explicity deny the certificate via the callback, otherwise memory | 296 // Need to explicity deny the certificate via the callback, otherwise memory |
245 // is leaked. | 297 // is leaked. |
246 if (!callback_.is_null()) { | 298 if (!callback_.is_null()) { |
247 base::ResetAndReturn(&callback_).Run(false); | 299 base::ResetAndReturn(&callback_).Run(false); |
248 } | 300 } |
249 web_contents_->RemoveUserData(UserDataKey()); | 301 web_contents_->RemoveUserData(UserDataKey()); |
250 } | 302 } |
| 303 |
| 304 void SSLErrorHandler::CommonNameMismatchHandlerCallback( |
| 305 const CommonNameMismatchHandler::Results& results) { |
| 306 timer_.Stop(); |
| 307 if (results.result == CommonNameMismatchHandler::SuggestedUrlCheckResult:: |
| 308 RESULT_SUGGESTED_URL_VALID) { |
| 309 ShowSSLInterstitial(results.suggested_url); |
| 310 } else { |
| 311 ShowSSLInterstitial(GURL()); |
| 312 } |
| 313 } |
OLD | NEW |