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 |
24 | 25 |
25 namespace { | 26 namespace { |
26 | 27 |
27 // The type of the delay before displaying the SSL interstitial. This can be | 28 // The type of the delay before displaying the SSL interstitial. This can be |
28 // changed in tests. | 29 // changed in tests. |
29 SSLErrorHandler::InterstitialDelayType g_interstitial_delay_type = | 30 SSLErrorHandler::InterstitialDelayType g_interstitial_delay_type = |
30 SSLErrorHandler::NORMAL; | 31 SSLErrorHandler::NORMAL; |
31 | 32 |
32 // Callback to call when the interstitial timer is started. Used for testing. | 33 // Callback to call when the interstitial timer is started. Used for testing. |
33 SSLErrorHandler::TimerStartedCallback* g_timer_started_callback = nullptr; | 34 SSLErrorHandler::TimerStartedCallback* g_timer_started_callback = nullptr; |
34 | 35 |
35 // Events for UMA. | 36 // Events for UMA. |
36 enum SSLErrorHandlerEvent { | 37 enum SSLErrorHandlerEvent { |
37 HANDLE_ALL, | 38 HANDLE_ALL, |
38 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE, | 39 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE, |
39 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE, | 40 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE, |
40 SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE, | 41 SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE, |
41 SHOW_SSL_INTERSTITIAL_OVERRIDABLE, | 42 SHOW_SSL_INTERSTITIAL_OVERRIDABLE, |
43 SHOW_COMMON_NAME_MISMATCH_INTERSTITIAL_NONOVERRIDABLE, | |
44 SHOW_COMMON_NAME_MISMATCH_INTERSTITIAL_OVERRIDABLE, | |
42 SSL_ERROR_HANDLER_EVENT_COUNT | 45 SSL_ERROR_HANDLER_EVENT_COUNT |
43 }; | 46 }; |
44 | 47 |
45 void RecordUMA(SSLErrorHandlerEvent event) { | 48 void RecordUMA(SSLErrorHandlerEvent event) { |
46 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_handler", | 49 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_handler", |
47 event, | 50 event, |
48 SSL_ERROR_HANDLER_EVENT_COUNT); | 51 SSL_ERROR_HANDLER_EVENT_COUNT); |
49 } | 52 } |
50 | 53 |
51 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
52 // The delay before displaying the SSL interstitial for cert errors. | 54 // The delay before displaying the SSL interstitial for cert errors. |
53 // - If a "captive portal detected" result arrives in this many seconds, | 55 // - If a "captive portal detected" or "suggested URL valid" result |
54 // a captive portal interstitial is displayed. | 56 // arrives in this many seconds, then a captive portal interstitial |
57 // or a common name mismatch interstitial is displayed. | |
55 // - Otherwise, an SSL interstitial is displayed. | 58 // - Otherwise, an SSL interstitial is displayed. |
56 const int kDefaultInterstitialDisplayDelayInSeconds = 2; | 59 const int kDefaultInterstitialDisplayDelayInSeconds = 2; |
57 | 60 |
58 base::TimeDelta GetInterstitialDisplayDelay( | 61 base::TimeDelta GetInterstitialDisplayDelay( |
59 SSLErrorHandler::InterstitialDelayType delay) { | 62 SSLErrorHandler::InterstitialDelayType delay) { |
60 switch (delay) { | 63 switch (delay) { |
61 case SSLErrorHandler::LONG: | 64 case SSLErrorHandler::LONG: |
62 return base::TimeDelta::FromHours(1); | 65 return base::TimeDelta::FromHours(1); |
63 | 66 |
64 case SSLErrorHandler::NONE: | 67 case SSLErrorHandler::NONE: |
65 return base::TimeDelta(); | 68 return base::TimeDelta(); |
66 | 69 |
67 case SSLErrorHandler::NORMAL: | 70 case SSLErrorHandler::NORMAL: |
68 return base::TimeDelta::FromSeconds( | 71 return base::TimeDelta::FromSeconds( |
69 kDefaultInterstitialDisplayDelayInSeconds); | 72 kDefaultInterstitialDisplayDelayInSeconds); |
70 | 73 |
71 default: | 74 default: |
72 NOTREACHED(); | 75 NOTREACHED(); |
73 } | 76 } |
74 return base::TimeDelta(); | 77 return base::TimeDelta(); |
75 } | 78 } |
76 | 79 |
80 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | |
77 bool IsCaptivePortalInterstitialEnabled() { | 81 bool IsCaptivePortalInterstitialEnabled() { |
78 return base::FieldTrialList::FindFullName("CaptivePortalInterstitial") == | 82 return base::FieldTrialList::FindFullName("CaptivePortalInterstitial") == |
79 "Enabled"; | 83 "Enabled"; |
80 } | 84 } |
81 #endif | 85 #endif |
82 | 86 |
83 } // namespace | 87 } // namespace |
84 | 88 |
85 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler); | 89 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler); |
86 | 90 |
87 void SSLErrorHandler::HandleSSLError( | 91 void SSLErrorHandler::HandleSSLError( |
88 content::WebContents* web_contents, | 92 content::WebContents* web_contents, |
89 int cert_error, | 93 int cert_error, |
90 const net::SSLInfo& ssl_info, | 94 const net::SSLInfo& ssl_info, |
91 const GURL& request_url, | 95 const GURL& request_url, |
92 int options_mask, | 96 int options_mask, |
93 scoped_ptr<SSLCertReporter> ssl_cert_reporter, | 97 scoped_ptr<SSLCertReporter> ssl_cert_reporter, |
94 const base::Callback<void(bool)>& callback) { | 98 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)); | 99 DCHECK(!FromWebContents(web_contents)); |
103 web_contents->SetUserData( | 100 web_contents->SetUserData( |
104 UserDataKey(), | 101 UserDataKey(), |
105 new SSLErrorHandler(web_contents, cert_error, ssl_info, request_url, | 102 new SSLErrorHandler(web_contents, cert_error, ssl_info, request_url, |
106 options_mask, ssl_cert_reporter.Pass(), callback)); | 103 options_mask, ssl_cert_reporter.Pass(), callback)); |
107 | 104 |
108 SSLErrorHandler* error_handler = | 105 SSLErrorHandler* error_handler = |
109 SSLErrorHandler::FromWebContents(web_contents); | 106 SSLErrorHandler::FromWebContents(web_contents); |
110 error_handler->StartHandlingError(); | 107 error_handler->StartHandlingError(); |
111 } | 108 } |
(...skipping 18 matching lines...) Expand all Loading... | |
130 int options_mask, | 127 int options_mask, |
131 scoped_ptr<SSLCertReporter> ssl_cert_reporter, | 128 scoped_ptr<SSLCertReporter> ssl_cert_reporter, |
132 const base::Callback<void(bool)>& callback) | 129 const base::Callback<void(bool)>& callback) |
133 : content::WebContentsObserver(web_contents), | 130 : content::WebContentsObserver(web_contents), |
134 web_contents_(web_contents), | 131 web_contents_(web_contents), |
135 cert_error_(cert_error), | 132 cert_error_(cert_error), |
136 ssl_info_(ssl_info), | 133 ssl_info_(ssl_info), |
137 request_url_(request_url), | 134 request_url_(request_url), |
138 options_mask_(options_mask), | 135 options_mask_(options_mask), |
139 callback_(callback), | 136 callback_(callback), |
140 ssl_cert_reporter_(ssl_cert_reporter.Pass()) { | 137 profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())), |
141 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | 138 ssl_cert_reporter_(ssl_cert_reporter.Pass()) {} |
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 } | |
149 | 139 |
150 SSLErrorHandler::~SSLErrorHandler() { | 140 SSLErrorHandler::~SSLErrorHandler() { |
151 } | 141 } |
152 | 142 |
153 void SSLErrorHandler::StartHandlingError() { | 143 void SSLErrorHandler::StartHandlingError() { |
154 RecordUMA(HANDLE_ALL); | 144 RecordUMA(HANDLE_ALL); |
155 | 145 |
146 std::vector<std::string> dns_names; | |
147 ssl_info_.cert->GetDNSNames(&dns_names); | |
148 DCHECK(!dns_names.empty()); | |
149 GURL suggested_url; | |
150 if (GetSuggestedUrl(dns_names, &suggested_url)) { | |
151 net::CertStatus extra_cert_errors = | |
152 ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID; | |
davidben
2015/08/17 18:59:19
What if CERT_STATUS_COMMON_NAME_INVALID isn't in c
Bhanu Dev
2015/08/18 05:09:10
The cert_status must have CERT_STATUS_COMMON_NAME_
| |
153 | |
154 // Show the SSL intersitial if |CERT_STATUS_COMMON_NAME_INVALID| is not | |
155 // the only error. Need not check for captive portal in this case. | |
156 // (See the comment below). | |
157 if (net::IsCertStatusError(extra_cert_errors) && | |
158 !net::IsCertStatusMinorError(ssl_info_.cert_status)) { | |
159 ShowSSLInterstitial(); | |
160 return; | |
161 } | |
162 CheckSuggestedUrl(suggested_url); | |
163 timer_.Start(FROM_HERE, | |
164 GetInterstitialDisplayDelay(g_interstitial_delay_type), this, | |
165 &SSLErrorHandler::OnTimerExpired); | |
166 if (g_timer_started_callback) | |
167 g_timer_started_callback->Run(web_contents_); | |
168 | |
169 // Do not check for a captive portal in this case, because a captive | |
170 // portal most likely cannot serve a valid certificate which passes the | |
171 // similarity check. | |
172 return; | |
173 } | |
174 | |
156 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | 175 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) |
176 CaptivePortalTabHelper* captive_portal_tab_helper = | |
177 CaptivePortalTabHelper::FromWebContents(web_contents_); | |
178 if (captive_portal_tab_helper) { | |
179 captive_portal_tab_helper->OnSSLCertError(ssl_info_); | |
180 } | |
181 | |
182 registrar_.Add(this, chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, | |
183 content::Source<Profile>(profile_)); | |
184 | |
157 if (IsCaptivePortalInterstitialEnabled()) { | 185 if (IsCaptivePortalInterstitialEnabled()) { |
158 CheckForCaptivePortal(); | 186 CheckForCaptivePortal(); |
159 timer_.Start(FROM_HERE, | 187 timer_.Start(FROM_HERE, |
160 GetInterstitialDisplayDelay(g_interstitial_delay_type), | 188 GetInterstitialDisplayDelay(g_interstitial_delay_type), |
161 this, &SSLErrorHandler::OnTimerExpired); | 189 this, &SSLErrorHandler::OnTimerExpired); |
162 if (g_timer_started_callback) | 190 if (g_timer_started_callback) |
163 g_timer_started_callback->Run(web_contents_); | 191 g_timer_started_callback->Run(web_contents_); |
164 return; | 192 return; |
165 } | 193 } |
166 #endif | 194 #endif |
167 // Display an SSL interstitial. | 195 // Display an SSL interstitial. |
168 ShowSSLInterstitial(); | 196 ShowSSLInterstitial(); |
169 } | 197 } |
170 | 198 |
171 void SSLErrorHandler::OnTimerExpired() { | 199 void SSLErrorHandler::OnTimerExpired() { |
172 ShowSSLInterstitial(); | 200 ShowSSLInterstitial(); |
173 } | 201 } |
174 | 202 |
203 bool SSLErrorHandler::GetSuggestedUrl(const std::vector<std::string>& dns_names, | |
204 GURL* suggested_url) const { | |
205 return CommonNameMismatchHandler::GetSuggestedUrl(request_url_, dns_names, | |
206 suggested_url); | |
207 } | |
208 | |
209 void SSLErrorHandler::CheckSuggestedUrl(const GURL& suggested_url) { | |
210 scoped_refptr<net::URLRequestContextGetter> request_context( | |
211 profile_->GetRequestContext()); | |
212 common_name_mismatch_handler_.reset( | |
213 new CommonNameMismatchHandler(request_url_, request_context)); | |
214 | |
215 common_name_mismatch_handler_->CheckSuggestedUrl( | |
216 suggested_url, | |
217 base::Bind(&SSLErrorHandler::CommonNameMismatchHandlerCallback, | |
218 base::Unretained(this))); | |
219 } | |
220 | |
221 void SSLErrorHandler::NavigateToSuggestedURL(const GURL& suggested_url) { | |
222 content::NavigationController::LoadURLParams load_params(suggested_url); | |
223 load_params.transition_type = ui::PAGE_TRANSITION_TYPED; | |
davidben
2015/08/17 18:59:19
This seems off. The URL wasn't typed into the omni
Bhanu Dev
2015/08/18 05:09:10
Used this since, https://code.google.com/p/chromiu
| |
224 web_contents()->GetController().LoadURLWithParams(load_params); | |
225 } | |
226 | |
175 void SSLErrorHandler::CheckForCaptivePortal() { | 227 void SSLErrorHandler::CheckForCaptivePortal() { |
176 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | 228 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) |
177 Profile* profile = Profile::FromBrowserContext( | |
178 web_contents_->GetBrowserContext()); | |
179 CaptivePortalService* captive_portal_service = | 229 CaptivePortalService* captive_portal_service = |
180 CaptivePortalServiceFactory::GetForProfile(profile); | 230 CaptivePortalServiceFactory::GetForProfile(profile_); |
181 captive_portal_service->DetectCaptivePortal(); | 231 captive_portal_service->DetectCaptivePortal(); |
182 #else | 232 #else |
183 NOTREACHED(); | 233 NOTREACHED(); |
184 #endif | 234 #endif |
185 } | 235 } |
186 | 236 |
187 void SSLErrorHandler::ShowCaptivePortalInterstitial(const GURL& landing_url) { | 237 void SSLErrorHandler::ShowCaptivePortalInterstitial(const GURL& landing_url) { |
188 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | 238 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) |
189 // Show captive portal blocking page. The interstitial owns the blocking page. | 239 // Show captive portal blocking page. The interstitial owns the blocking page. |
190 const Profile* const profile = | 240 RecordUMA(SSLBlockingPage::IsOverridable(options_mask_, profile_) |
191 Profile::FromBrowserContext(web_contents_->GetBrowserContext()); | |
192 RecordUMA(SSLBlockingPage::IsOverridable(options_mask_, profile) | |
193 ? SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE | 241 ? SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE |
194 : SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE); | 242 : SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE); |
195 (new CaptivePortalBlockingPage(web_contents_, request_url_, landing_url, | 243 (new CaptivePortalBlockingPage(web_contents_, request_url_, landing_url, |
196 ssl_cert_reporter_.Pass(), ssl_info_, | 244 ssl_cert_reporter_.Pass(), ssl_info_, |
197 callback_))->Show(); | 245 callback_))->Show(); |
198 // Once an interstitial is displayed, no need to keep the handler around. | 246 // Once an interstitial is displayed, no need to keep the handler around. |
199 // This is the equivalent of "delete this". | 247 // This is the equivalent of "delete this". |
200 web_contents_->RemoveUserData(UserDataKey()); | 248 web_contents_->RemoveUserData(UserDataKey()); |
201 #else | 249 #else |
202 NOTREACHED(); | 250 NOTREACHED(); |
203 #endif | 251 #endif |
204 } | 252 } |
205 | 253 |
206 void SSLErrorHandler::ShowSSLInterstitial() { | 254 void SSLErrorHandler::ShowSSLInterstitial() { |
207 // Show SSL blocking page. The interstitial owns the blocking page. | 255 // Show SSL blocking page. The interstitial owns the blocking page. |
208 const Profile* const profile = | 256 RecordUMA(SSLBlockingPage::IsOverridable(options_mask_, profile_) |
209 Profile::FromBrowserContext(web_contents_->GetBrowserContext()); | |
210 RecordUMA(SSLBlockingPage::IsOverridable(options_mask_, profile) | |
211 ? SHOW_SSL_INTERSTITIAL_OVERRIDABLE | 257 ? SHOW_SSL_INTERSTITIAL_OVERRIDABLE |
212 : SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE); | 258 : SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE); |
259 | |
213 (new SSLBlockingPage(web_contents_, cert_error_, ssl_info_, request_url_, | 260 (new SSLBlockingPage(web_contents_, cert_error_, ssl_info_, request_url_, |
214 options_mask_, base::Time::NowFromSystemTime(), | 261 options_mask_, base::Time::NowFromSystemTime(), |
215 ssl_cert_reporter_.Pass(), callback_))->Show(); | 262 ssl_cert_reporter_.Pass(), callback_)) |
263 ->Show(); | |
216 // Once an interstitial is displayed, no need to keep the handler around. | 264 // Once an interstitial is displayed, no need to keep the handler around. |
217 // This is the equivalent of "delete this". | 265 // This is the equivalent of "delete this". |
218 web_contents_->RemoveUserData(UserDataKey()); | 266 web_contents_->RemoveUserData(UserDataKey()); |
219 } | 267 } |
220 | 268 |
221 void SSLErrorHandler::Observe( | 269 void SSLErrorHandler::Observe( |
222 int type, | 270 int type, |
223 const content::NotificationSource& source, | 271 const content::NotificationSource& source, |
224 const content::NotificationDetails& details) { | 272 const content::NotificationDetails& details) { |
225 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) | 273 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION) |
(...skipping 22 matching lines...) Expand all Loading... | |
248 // Destroy the error handler when the page load is stopped. | 296 // Destroy the error handler when the page load is stopped. |
249 DeleteSSLErrorHandler(); | 297 DeleteSSLErrorHandler(); |
250 } | 298 } |
251 | 299 |
252 void SSLErrorHandler::DeleteSSLErrorHandler() { | 300 void SSLErrorHandler::DeleteSSLErrorHandler() { |
253 // Need to explicity deny the certificate via the callback, otherwise memory | 301 // Need to explicity deny the certificate via the callback, otherwise memory |
254 // is leaked. | 302 // is leaked. |
255 if (!callback_.is_null()) { | 303 if (!callback_.is_null()) { |
256 base::ResetAndReturn(&callback_).Run(false); | 304 base::ResetAndReturn(&callback_).Run(false); |
257 } | 305 } |
306 if (common_name_mismatch_handler_) { | |
307 common_name_mismatch_handler_->Cancel(); | |
308 common_name_mismatch_handler_.reset(); | |
309 } | |
258 web_contents_->RemoveUserData(UserDataKey()); | 310 web_contents_->RemoveUserData(UserDataKey()); |
259 } | 311 } |
312 | |
313 void SSLErrorHandler::CommonNameMismatchHandlerCallback( | |
314 const CommonNameMismatchHandler::SuggestedUrlCheckResult& result, | |
315 const GURL& suggested_url) { | |
316 timer_.Stop(); | |
317 if (result == CommonNameMismatchHandler::SuggestedUrlCheckResult:: | |
318 SUGGESTED_URL_AVAILABLE) { | |
319 NavigateToSuggestedURL(suggested_url); | |
320 } else { | |
321 ShowSSLInterstitial(); | |
322 } | |
323 } | |
OLD | NEW |