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

Side by Side Diff: chrome/browser/ssl/ssl_error_handler.cc

Issue 2620203003: Add initial version of captive portal list checking. (Closed)
Patch Set: Created 3 years, 11 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 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 <stdint.h> 7 #include <stdint.h>
8 #include <unordered_set>
8 #include <utility> 9 #include <utility>
9 10
10 #include "base/callback_helpers.h" 11 #include "base/callback_helpers.h"
11 #include "base/feature_list.h" 12 #include "base/feature_list.h"
13 #include "base/files/file_util.h"
12 #include "base/lazy_instance.h" 14 #include "base/lazy_instance.h"
13 #include "base/macros.h" 15 #include "base/macros.h"
14 #include "base/metrics/histogram_macros.h" 16 #include "base/metrics/histogram_macros.h"
15 #include "base/strings/stringprintf.h" 17 #include "base/strings/stringprintf.h"
16 #include "base/threading/non_thread_safe.h" 18 #include "base/threading/non_thread_safe.h"
17 #include "base/time/clock.h" 19 #include "base/time/clock.h"
18 #include "base/time/time.h" 20 #include "base/time/time.h"
19 #include "chrome/browser/browser_process.h" 21 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/profiles/profile.h" 22 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ssl/bad_clock_blocking_page.h" 23 #include "chrome/browser/ssl/bad_clock_blocking_page.h"
22 #include "chrome/browser/ssl/ssl_blocking_page.h" 24 #include "chrome/browser/ssl/ssl_blocking_page.h"
23 #include "chrome/browser/ssl/ssl_cert_reporter.h" 25 #include "chrome/browser/ssl/ssl_cert_reporter.h"
26 #include "chrome/browser/ssl/tls_error_assistant.pb.h"
24 #include "chrome/common/features.h" 27 #include "chrome/common/features.h"
28 #include "chrome/grit/browser_resources.h"
25 #include "components/network_time/network_time_tracker.h" 29 #include "components/network_time/network_time_tracker.h"
26 #include "components/ssl_errors/error_classification.h" 30 #include "components/ssl_errors/error_classification.h"
27 #include "components/ssl_errors/error_info.h" 31 #include "components/ssl_errors/error_info.h"
28 #include "content/public/browser/notification_service.h" 32 #include "content/public/browser/notification_service.h"
29 #include "content/public/browser/notification_source.h" 33 #include "content/public/browser/notification_source.h"
30 #include "content/public/browser/render_frame_host.h" 34 #include "content/public/browser/render_frame_host.h"
31 #include "content/public/browser/web_contents.h" 35 #include "content/public/browser/web_contents.h"
32 #include "net/base/net_errors.h" 36 #include "net/base/net_errors.h"
37 #include "ui/base/resource/resource_bundle.h"
33 38
34 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) 39 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
35 #include "chrome/browser/captive_portal/captive_portal_service.h" 40 #include "chrome/browser/captive_portal/captive_portal_service.h"
36 #include "chrome/browser/captive_portal/captive_portal_service_factory.h" 41 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
37 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h" 42 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
38 #include "chrome/browser/ssl/captive_portal_blocking_page.h" 43 #include "chrome/browser/ssl/captive_portal_blocking_page.h"
39 #endif 44 #endif
40 45
41 namespace { 46 namespace {
42 47
43 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) 48 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
44 const base::Feature kCaptivePortalInterstitial{ 49 const base::Feature kCaptivePortalInterstitial{
45 "CaptivePortalInterstitial", base::FEATURE_ENABLED_BY_DEFAULT}; 50 "CaptivePortalInterstitial", base::FEATURE_ENABLED_BY_DEFAULT};
46 #endif 51 #endif
47 52
48 const base::Feature kSSLCommonNameMismatchHandling{ 53 const base::Feature kSSLCommonNameMismatchHandling{
49 "SSLCommonNameMismatchHandling", base::FEATURE_ENABLED_BY_DEFAULT}; 54 "SSLCommonNameMismatchHandling", base::FEATURE_ENABLED_BY_DEFAULT};
50 55
51 // Default delay in milliseconds before displaying the SSL interstitial. 56 // Default delay in milliseconds before displaying the SSL interstitial.
52 // This can be changed in tests. 57 // This can be changed in tests.
53 // - If there is a name mismatch and a suggested URL available result arrives 58 // - 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. 59 // during this time, the user is redirected to the suggester URL.
55 // - If a "captive portal detected" result arrives during this time, 60 // - If a "captive portal detected" result arrives during this time,
56 // a captive portal interstitial is displayed. 61 // a captive portal interstitial is displayed.
57 // - Otherwise, an SSL interstitial is displayed. 62 // - Otherwise, an SSL interstitial is displayed.
58 const int64_t kInterstitialDelayInMilliseconds = 3000; 63 const int64_t kInterstitialDelayInMilliseconds = 3000;
59 64
60 // Events for UMA. 65 const char kHistogram[] = "interstitial.ssl_error_handler";
61 enum SSLErrorHandlerEvent {
62 HANDLE_ALL,
63 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE,
64 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE,
65 SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE,
66 SHOW_SSL_INTERSTITIAL_OVERRIDABLE,
67 WWW_MISMATCH_FOUND,
68 WWW_MISMATCH_URL_AVAILABLE,
69 WWW_MISMATCH_URL_NOT_AVAILABLE,
70 SHOW_BAD_CLOCK,
71 SSL_ERROR_HANDLER_EVENT_COUNT
72 };
73 66
74 // Adds a message to console after navigation commits and then, deletes itself. 67 // Adds a message to console after navigation commits and then, deletes itself.
75 // Also deletes itself if the navigation is stopped. 68 // Also deletes itself if the navigation is stopped.
76 class CommonNameMismatchRedirectObserver 69 class CommonNameMismatchRedirectObserver
77 : public content::WebContentsObserver, 70 : public content::WebContentsObserver,
78 public content::WebContentsUserData<CommonNameMismatchRedirectObserver> { 71 public content::WebContentsUserData<CommonNameMismatchRedirectObserver> {
79 public: 72 public:
80 static void AddToConsoleAfterNavigation( 73 static void AddToConsoleAfterNavigation(
81 content::WebContents* web_contents, 74 content::WebContents* web_contents,
82 const std::string& request_url_hostname, 75 const std::string& request_url_hostname,
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 web_contents_->RemoveUserData(UserDataKey()); 114 web_contents_->RemoveUserData(UserDataKey());
122 } 115 }
123 116
124 content::WebContents* web_contents_; 117 content::WebContents* web_contents_;
125 const std::string request_url_hostname_; 118 const std::string request_url_hostname_;
126 const std::string suggested_url_hostname_; 119 const std::string suggested_url_hostname_;
127 120
128 DISALLOW_COPY_AND_ASSIGN(CommonNameMismatchRedirectObserver); 121 DISALLOW_COPY_AND_ASSIGN(CommonNameMismatchRedirectObserver);
129 }; 122 };
130 123
131 void RecordUMA(SSLErrorHandlerEvent event) { 124 void RecordUMA(SSLErrorHandler::UMAEvent event) {
132 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_handler", event, 125 UMA_HISTOGRAM_ENUMERATION(kHistogram, event,
133 SSL_ERROR_HANDLER_EVENT_COUNT); 126 SSLErrorHandler::SSL_ERROR_HANDLER_EVENT_COUNT);
134 } 127 }
135 128
136 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) 129 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
137 bool IsCaptivePortalInterstitialEnabled() { 130 bool IsCaptivePortalInterstitialEnabled() {
138 return base::FeatureList::IsEnabled(kCaptivePortalInterstitial); 131 return base::FeatureList::IsEnabled(kCaptivePortalInterstitial);
139 } 132 }
140 #endif 133 #endif
141 134
142 bool IsSSLCommonNameMismatchHandlingEnabled() { 135 bool IsSSLCommonNameMismatchHandlingEnabled() {
143 return base::FeatureList::IsEnabled(kSSLCommonNameMismatchHandling); 136 return base::FeatureList::IsEnabled(kSSLCommonNameMismatchHandling);
144 } 137 }
145 138
139 // Reads the TLS error assistant configuration from the resource bundle.
140 void ReadErrorAssistantProtoFromResourceBundle(std::string* binary_pb) {
141 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
142 bundle.GetRawDataResource(IDR_TLS_ERROR_ASSISTANT_PB).CopyToString(binary_pb);
143 }
144
145 std::unique_ptr<std::unordered_set<std::string>> LoadCaptivePortalCertHashes(
146 const chrome_browser_ssl::TLSErrorAssistantConfig& proto) {
147 std::unique_ptr<std::unordered_set<std::string>> hashes(
148 new std::unordered_set<std::string>());
149 for (const chrome_browser_ssl::CaptivePortalCert& cert :
150 proto.captive_portal_cert()) {
151 hashes.get()->insert(cert.sha256_hash());
152 }
153 return hashes;
154 }
155
146 // Configuration for SSLErrorHandler. 156 // Configuration for SSLErrorHandler.
147 class ConfigSingleton : public base::NonThreadSafe { 157 class ConfigSingleton : public base::NonThreadSafe {
148 public: 158 public:
149 ConfigSingleton(); 159 ConfigSingleton();
150 160
151 base::TimeDelta interstitial_delay() const; 161 base::TimeDelta interstitial_delay() const;
152 SSLErrorHandler::TimerStartedCallback* timer_started_callback() const; 162 SSLErrorHandler::TimerStartedCallback* timer_started_callback() const;
153 base::Clock* clock() const; 163 base::Clock* clock() const;
154 network_time::NetworkTimeTracker* network_time_tracker() const; 164 network_time::NetworkTimeTracker* network_time_tracker() const;
155 165
166 // Returns true if one of the cert hashes in |ssl_info| is of a captive portal
estark 2017/01/12 18:54:43 Haven't read the implementation yet, but this make
meacer 2017/01/12 23:54:03 It's intentional for this draft in the sense that
estark 2017/01/13 23:40:23 Hmmm. From my reading of ssl_info.h, I would guess
meacer 2017/01/20 21:29:59 Added a note to tls_error_assistant.proto. In the
167 // certificate. The set of captive portal hashes is loaded on first use.
168 bool IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info);
169
170 // Testing methods:
156 void SetInterstitialDelayForTesting(const base::TimeDelta& delay); 171 void SetInterstitialDelayForTesting(const base::TimeDelta& delay);
157 void SetTimerStartedCallbackForTesting( 172 void SetTimerStartedCallbackForTesting(
158 SSLErrorHandler::TimerStartedCallback* callback); 173 SSLErrorHandler::TimerStartedCallback* callback);
159 void SetClockForTesting(base::Clock* clock); 174 void SetClockForTesting(base::Clock* clock);
160 void SetNetworkTimeTrackerForTesting( 175 void SetNetworkTimeTrackerForTesting(
161 network_time::NetworkTimeTracker* tracker); 176 network_time::NetworkTimeTracker* tracker);
177 void SetErrorAssistantProtoForTesting(
178 const chrome_browser_ssl::TLSErrorAssistantConfig& error_assistant_proto);
162 179
163 private: 180 private:
164 base::TimeDelta interstitial_delay_; 181 base::TimeDelta interstitial_delay_;
165 182
166 // Callback to call when the interstitial timer is started. Used for 183 // Callback to call when the interstitial timer is started. Used for
167 // testing. 184 // testing.
168 SSLErrorHandler::TimerStartedCallback* timer_started_callback_ = nullptr; 185 SSLErrorHandler::TimerStartedCallback* timer_started_callback_ = nullptr;
169 186
170 // The clock to use when deciding which error type to display. Used for 187 // The clock to use when deciding which error type to display. Used for
171 // testing. 188 // testing.
172 base::Clock* testing_clock_ = nullptr; 189 base::Clock* testing_clock_ = nullptr;
173 190
174 network_time::NetworkTimeTracker* network_time_tracker_ = nullptr; 191 network_time::NetworkTimeTracker* network_time_tracker_ = nullptr;
192
193 // SPKI hashes belonging to certs treated as captive portal portals. Populated
estark 2017/01/12 18:54:43 nit: extra "portal"
meacer 2017/01/20 21:29:59 Done.
194 // the first time IsKnownCaptivePortalCert() or
195 // SetErrorAssistantProtoForTesting() is called.
196 std::unique_ptr<std::unordered_set<std::string>> captive_portal_spki_hashes_;
175 }; 197 };
176 198
177 ConfigSingleton::ConfigSingleton() 199 ConfigSingleton::ConfigSingleton()
178 : interstitial_delay_( 200 : interstitial_delay_(
179 base::TimeDelta::FromMilliseconds(kInterstitialDelayInMilliseconds)) { 201 base::TimeDelta::FromMilliseconds(kInterstitialDelayInMilliseconds)) {
180 } 202 }
181 203
182 base::TimeDelta ConfigSingleton::interstitial_delay() const { 204 base::TimeDelta ConfigSingleton::interstitial_delay() const {
183 return interstitial_delay_; 205 return interstitial_delay_;
184 } 206 }
(...skipping 26 matching lines...) Expand all
211 233
212 void ConfigSingleton::SetClockForTesting(base::Clock* clock) { 234 void ConfigSingleton::SetClockForTesting(base::Clock* clock) {
213 testing_clock_ = clock; 235 testing_clock_ = clock;
214 } 236 }
215 237
216 void ConfigSingleton::SetNetworkTimeTrackerForTesting( 238 void ConfigSingleton::SetNetworkTimeTrackerForTesting(
217 network_time::NetworkTimeTracker* tracker) { 239 network_time::NetworkTimeTracker* tracker) {
218 network_time_tracker_ = tracker; 240 network_time_tracker_ = tracker;
219 } 241 }
220 242
243 void ConfigSingleton::SetErrorAssistantProtoForTesting(
244 const chrome_browser_ssl::TLSErrorAssistantConfig& error_assistant_proto) {
245 DCHECK(!captive_portal_spki_hashes_);
246 captive_portal_spki_hashes_ =
247 LoadCaptivePortalCertHashes(error_assistant_proto);
248 }
249
250 bool ConfigSingleton::IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info) {
251 if (!captive_portal_spki_hashes_) {
252 std::string binary_proto;
253 ReadErrorAssistantProtoFromResourceBundle(&binary_proto);
254 chrome_browser_ssl::TLSErrorAssistantConfig proto;
255 proto.ParseFromString(binary_proto);
256 captive_portal_spki_hashes_ = LoadCaptivePortalCertHashes(proto);
257 }
258
259 for (const net::HashValue& hash_value : ssl_info.public_key_hashes) {
estark 2017/01/12 18:54:43 Ok, now that I got down here, it looks like we're
meacer 2017/01/12 23:54:03 Can we not assume the first or the last two of the
meacer 2017/01/20 21:29:59 Added sha256 check and clarified comment in line 1
260 if (captive_portal_spki_hashes_->find(hash_value.ToString()) !=
261 captive_portal_spki_hashes_->end()) {
262 return true;
263 }
264 }
265 return false;
266 }
267
221 static base::LazyInstance<ConfigSingleton>::Leaky g_config = 268 static base::LazyInstance<ConfigSingleton>::Leaky g_config =
222 LAZY_INSTANCE_INITIALIZER; 269 LAZY_INSTANCE_INITIALIZER;
223 270
224 } // namespace 271 } // namespace
225 272
226 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler); 273 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler);
227 DEFINE_WEB_CONTENTS_USER_DATA_KEY(CommonNameMismatchRedirectObserver); 274 DEFINE_WEB_CONTENTS_USER_DATA_KEY(CommonNameMismatchRedirectObserver);
228 275
229 void SSLErrorHandler::HandleSSLError( 276 void SSLErrorHandler::HandleSSLError(
230 content::WebContents* web_contents, 277 content::WebContents* web_contents,
(...skipping 28 matching lines...) Expand all
259 void SSLErrorHandler::SetClockForTesting(base::Clock* testing_clock) { 306 void SSLErrorHandler::SetClockForTesting(base::Clock* testing_clock) {
260 g_config.Pointer()->SetClockForTesting(testing_clock); 307 g_config.Pointer()->SetClockForTesting(testing_clock);
261 } 308 }
262 309
263 // static 310 // static
264 void SSLErrorHandler::SetNetworkTimeTrackerForTesting( 311 void SSLErrorHandler::SetNetworkTimeTrackerForTesting(
265 network_time::NetworkTimeTracker* tracker) { 312 network_time::NetworkTimeTracker* tracker) {
266 g_config.Pointer()->SetNetworkTimeTrackerForTesting(tracker); 313 g_config.Pointer()->SetNetworkTimeTrackerForTesting(tracker);
267 } 314 }
268 315
316 // static
317 void SSLErrorHandler::SetErrorAssistantProtoForTesting(
318 const chrome_browser_ssl::TLSErrorAssistantConfig& config_proto) {
319 g_config.Pointer()->SetErrorAssistantProtoForTesting(config_proto);
320 }
321
322 // static
323 std::string SSLErrorHandler::GetHistogramNameForTesting() {
estark 2017/01/12 18:54:43 Ah, this is a good idea, I always end up repeating
meacer 2017/01/12 23:54:03 It always works until it doesn't.
324 return kHistogram;
325 }
326
269 SSLErrorHandler::SSLErrorHandler( 327 SSLErrorHandler::SSLErrorHandler(
270 content::WebContents* web_contents, 328 content::WebContents* web_contents,
271 int cert_error, 329 int cert_error,
272 const net::SSLInfo& ssl_info, 330 const net::SSLInfo& ssl_info,
273 const GURL& request_url, 331 const GURL& request_url,
274 int options_mask, 332 int options_mask,
275 std::unique_ptr<SSLCertReporter> ssl_cert_reporter, 333 std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
276 const base::Callback<void(content::CertificateRequestResultType)>& callback) 334 const base::Callback<void(content::CertificateRequestResultType)>& callback)
277 : content::WebContentsObserver(web_contents), 335 : content::WebContentsObserver(web_contents),
278 web_contents_(web_contents), 336 web_contents_(web_contents),
(...skipping 11 matching lines...) Expand all
290 348
291 void SSLErrorHandler::StartHandlingError() { 349 void SSLErrorHandler::StartHandlingError() {
292 RecordUMA(HANDLE_ALL); 350 RecordUMA(HANDLE_ALL);
293 351
294 if (ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error_) == 352 if (ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error_) ==
295 ssl_errors::ErrorInfo::CERT_DATE_INVALID) { 353 ssl_errors::ErrorInfo::CERT_DATE_INVALID) {
296 HandleCertDateInvalidError(); 354 HandleCertDateInvalidError();
297 return; 355 return;
298 } 356 }
299 357
358 if (cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID &&
359 g_config.Pointer()->IsKnownCaptivePortalCert(ssl_info_)) {
estark 2017/01/12 18:54:43 Maybe this should be Finchable? It would be good
meacer 2017/01/12 23:54:03 I was hoping to avoid another finch for this file,
360 RecordUMA(CAPTIVE_PORTAL_CERT_FOUND);
361 ShowCaptivePortalInterstitial(GURL());
estark 2017/01/12 18:54:43 How will this work without an empty landing URL? D
meacer 2017/01/12 23:54:03 Empty landing URL takes the user to the captive po
meacer 2017/01/20 21:29:59 Actually, this was buggy (the text was broken). I'
362 return;
363 }
364
300 std::vector<std::string> dns_names; 365 std::vector<std::string> dns_names;
301 ssl_info_.cert->GetDNSNames(&dns_names); 366 ssl_info_.cert->GetDNSNames(&dns_names);
302 DCHECK(!dns_names.empty()); 367 DCHECK(!dns_names.empty());
303 GURL suggested_url; 368 GURL suggested_url;
304 if (IsSSLCommonNameMismatchHandlingEnabled() && 369 if (IsSSLCommonNameMismatchHandlingEnabled() &&
305 cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID && 370 cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID &&
306 IsErrorOverridable() && GetSuggestedUrl(dns_names, &suggested_url)) { 371 IsErrorOverridable() && GetSuggestedUrl(dns_names, &suggested_url)) {
307 RecordUMA(WWW_MISMATCH_FOUND); 372 RecordUMA(WWW_MISMATCH_FOUND);
308 net::CertStatus extra_cert_errors = 373 net::CertStatus extra_cert_errors =
309 ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID; 374 ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID;
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after
538 network_time::NetworkTimeTracker* tracker = 603 network_time::NetworkTimeTracker* tracker =
539 g_config.Pointer()->network_time_tracker(); 604 g_config.Pointer()->network_time_tracker();
540 ssl_errors::ClockState clock_state = ssl_errors::GetClockState(now, tracker); 605 ssl_errors::ClockState clock_state = ssl_errors::GetClockState(now, tracker);
541 if (clock_state == ssl_errors::CLOCK_STATE_FUTURE || 606 if (clock_state == ssl_errors::CLOCK_STATE_FUTURE ||
542 clock_state == ssl_errors::CLOCK_STATE_PAST) { 607 clock_state == ssl_errors::CLOCK_STATE_PAST) {
543 ShowBadClockInterstitial(now, clock_state); 608 ShowBadClockInterstitial(now, clock_state);
544 return; // |this| is deleted after showing the interstitial. 609 return; // |this| is deleted after showing the interstitial.
545 } 610 }
546 ShowSSLInterstitial(); 611 ShowSSLInterstitial();
547 } 612 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698