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

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

Issue 2620203003: Add initial version of captive portal list checking. (Closed)
Patch Set: estark comments 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/navigation_handle.h" 32 #include "content/public/browser/navigation_handle.h"
29 #include "content/public/browser/notification_service.h" 33 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/notification_source.h" 34 #include "content/public/browser/notification_source.h"
31 #include "content/public/browser/render_frame_host.h" 35 #include "content/public/browser/render_frame_host.h"
32 #include "content/public/browser/web_contents.h" 36 #include "content/public/browser/web_contents.h"
33 #include "net/base/net_errors.h" 37 #include "net/base/net_errors.h"
38 #include "ui/base/resource/resource_bundle.h"
34 39
35 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) 40 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
36 #include "chrome/browser/captive_portal/captive_portal_service.h" 41 #include "chrome/browser/captive_portal/captive_portal_service.h"
37 #include "chrome/browser/captive_portal/captive_portal_service_factory.h" 42 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
38 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h" 43 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
39 #include "chrome/browser/ssl/captive_portal_blocking_page.h" 44 #include "chrome/browser/ssl/captive_portal_blocking_page.h"
40 #endif 45 #endif
41 46
42 namespace { 47 namespace {
43 48
44 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) 49 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
45 const base::Feature kCaptivePortalInterstitial{ 50 const base::Feature kCaptivePortalInterstitial{
46 "CaptivePortalInterstitial", base::FEATURE_ENABLED_BY_DEFAULT}; 51 "CaptivePortalInterstitial", base::FEATURE_ENABLED_BY_DEFAULT};
47 #endif 52 #endif
48 53
49 const base::Feature kSSLCommonNameMismatchHandling{ 54 const base::Feature kSSLCommonNameMismatchHandling{
50 "SSLCommonNameMismatchHandling", base::FEATURE_ENABLED_BY_DEFAULT}; 55 "SSLCommonNameMismatchHandling", base::FEATURE_ENABLED_BY_DEFAULT};
51 56
52 // Default delay in milliseconds before displaying the SSL interstitial. 57 // Default delay in milliseconds before displaying the SSL interstitial.
53 // This can be changed in tests. 58 // This can be changed in tests.
54 // - If there is a name mismatch and a suggested URL available result arrives 59 // - If there is a name mismatch and a suggested URL available result arrives
55 // during this time, the user is redirected to the suggester URL. 60 // during this time, the user is redirected to the suggester URL.
56 // - If a "captive portal detected" result arrives during this time, 61 // - If a "captive portal detected" result arrives during this time,
57 // a captive portal interstitial is displayed. 62 // a captive portal interstitial is displayed.
58 // - Otherwise, an SSL interstitial is displayed. 63 // - Otherwise, an SSL interstitial is displayed.
59 const int64_t kInterstitialDelayInMilliseconds = 3000; 64 const int64_t kInterstitialDelayInMilliseconds = 3000;
60 65
61 // Events for UMA. 66 const char kHistogram[] = "interstitial.ssl_error_handler";
62 enum SSLErrorHandlerEvent {
63 HANDLE_ALL,
64 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE,
65 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE,
66 SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE,
67 SHOW_SSL_INTERSTITIAL_OVERRIDABLE,
68 WWW_MISMATCH_FOUND,
69 WWW_MISMATCH_URL_AVAILABLE,
70 WWW_MISMATCH_URL_NOT_AVAILABLE,
71 SHOW_BAD_CLOCK,
72 SSL_ERROR_HANDLER_EVENT_COUNT
73 };
74 67
75 // Adds a message to console after navigation commits and then, deletes itself. 68 // Adds a message to console after navigation commits and then, deletes itself.
76 // Also deletes itself if the navigation is stopped. 69 // Also deletes itself if the navigation is stopped.
77 class CommonNameMismatchRedirectObserver 70 class CommonNameMismatchRedirectObserver
78 : public content::WebContentsObserver, 71 : public content::WebContentsObserver,
79 public content::WebContentsUserData<CommonNameMismatchRedirectObserver> { 72 public content::WebContentsUserData<CommonNameMismatchRedirectObserver> {
80 public: 73 public:
81 static void AddToConsoleAfterNavigation( 74 static void AddToConsoleAfterNavigation(
82 content::WebContents* web_contents, 75 content::WebContents* web_contents,
83 const std::string& request_url_hostname, 76 const std::string& request_url_hostname,
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 web_contents_->RemoveUserData(UserDataKey()); 115 web_contents_->RemoveUserData(UserDataKey());
123 } 116 }
124 117
125 content::WebContents* web_contents_; 118 content::WebContents* web_contents_;
126 const std::string request_url_hostname_; 119 const std::string request_url_hostname_;
127 const std::string suggested_url_hostname_; 120 const std::string suggested_url_hostname_;
128 121
129 DISALLOW_COPY_AND_ASSIGN(CommonNameMismatchRedirectObserver); 122 DISALLOW_COPY_AND_ASSIGN(CommonNameMismatchRedirectObserver);
130 }; 123 };
131 124
132 void RecordUMA(SSLErrorHandlerEvent event) { 125 void RecordUMA(SSLErrorHandler::UMAEvent event) {
133 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_handler", event, 126 UMA_HISTOGRAM_ENUMERATION(kHistogram, event,
134 SSL_ERROR_HANDLER_EVENT_COUNT); 127 SSLErrorHandler::SSL_ERROR_HANDLER_EVENT_COUNT);
135 } 128 }
136 129
137 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) 130 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
138 bool IsCaptivePortalInterstitialEnabled() { 131 bool IsCaptivePortalInterstitialEnabled() {
139 return base::FeatureList::IsEnabled(kCaptivePortalInterstitial); 132 return base::FeatureList::IsEnabled(kCaptivePortalInterstitial);
140 } 133 }
141 #endif 134 #endif
142 135
143 bool IsSSLCommonNameMismatchHandlingEnabled() { 136 bool IsSSLCommonNameMismatchHandlingEnabled() {
144 return base::FeatureList::IsEnabled(kSSLCommonNameMismatchHandling); 137 return base::FeatureList::IsEnabled(kSSLCommonNameMismatchHandling);
145 } 138 }
146 139
140 // Reads the TLS error assistant configuration from the resource bundle.
141 void ReadErrorAssistantProtoFromResourceBundle(std::string* binary_pb) {
142 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
143 bundle.GetRawDataResource(IDR_TLS_ERROR_ASSISTANT_PB).CopyToString(binary_pb);
estark 2017/01/20 23:31:18 Could you explain more why it's okay to load it on
meacer 2017/01/31 00:22:47 I couldn't find an authoritative answer to this, b
144 }
145
146 std::unique_ptr<std::unordered_set<std::string>> LoadCaptivePortalCertHashes(
147 const chrome_browser_ssl::TLSErrorAssistantConfig& proto) {
148 std::unique_ptr<std::unordered_set<std::string>> hashes(
149 new std::unordered_set<std::string>());
150 for (const chrome_browser_ssl::CaptivePortalCert& cert :
151 proto.captive_portal_cert()) {
152 hashes.get()->insert(cert.sha256_hash());
153 }
154 return hashes;
155 }
156
147 // Configuration for SSLErrorHandler. 157 // Configuration for SSLErrorHandler.
148 class ConfigSingleton : public base::NonThreadSafe { 158 class ConfigSingleton : public base::NonThreadSafe {
149 public: 159 public:
150 ConfigSingleton(); 160 ConfigSingleton();
151 161
152 base::TimeDelta interstitial_delay() const; 162 base::TimeDelta interstitial_delay() const;
153 SSLErrorHandler::TimerStartedCallback* timer_started_callback() const; 163 SSLErrorHandler::TimerStartedCallback* timer_started_callback() const;
154 base::Clock* clock() const; 164 base::Clock* clock() const;
155 network_time::NetworkTimeTracker* network_time_tracker() const; 165 network_time::NetworkTimeTracker* network_time_tracker() const;
156 166
167 // Returns true if any of the SHA256 hashes in |ssl_info| is of a captive
168 // portal certificate. The set of captive portal hashes is loaded on first
169 // use.
170 bool IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info);
171
172 // Testing methods:
157 void SetInterstitialDelayForTesting(const base::TimeDelta& delay); 173 void SetInterstitialDelayForTesting(const base::TimeDelta& delay);
158 void SetTimerStartedCallbackForTesting( 174 void SetTimerStartedCallbackForTesting(
159 SSLErrorHandler::TimerStartedCallback* callback); 175 SSLErrorHandler::TimerStartedCallback* callback);
160 void SetClockForTesting(base::Clock* clock); 176 void SetClockForTesting(base::Clock* clock);
161 void SetNetworkTimeTrackerForTesting( 177 void SetNetworkTimeTrackerForTesting(
162 network_time::NetworkTimeTracker* tracker); 178 network_time::NetworkTimeTracker* tracker);
179 void SetErrorAssistantProtoForTesting(
180 const chrome_browser_ssl::TLSErrorAssistantConfig& error_assistant_proto);
163 181
164 private: 182 private:
165 base::TimeDelta interstitial_delay_; 183 base::TimeDelta interstitial_delay_;
166 184
167 // Callback to call when the interstitial timer is started. Used for 185 // Callback to call when the interstitial timer is started. Used for
168 // testing. 186 // testing.
169 SSLErrorHandler::TimerStartedCallback* timer_started_callback_ = nullptr; 187 SSLErrorHandler::TimerStartedCallback* timer_started_callback_ = nullptr;
170 188
171 // The clock to use when deciding which error type to display. Used for 189 // The clock to use when deciding which error type to display. Used for
172 // testing. 190 // testing.
173 base::Clock* testing_clock_ = nullptr; 191 base::Clock* testing_clock_ = nullptr;
174 192
175 network_time::NetworkTimeTracker* network_time_tracker_ = nullptr; 193 network_time::NetworkTimeTracker* network_time_tracker_ = nullptr;
194
195 // SPKI hashes belonging to certs treated as captive portals. Populated the
estark 2017/01/20 23:31:18 nit: "Populated" => "Null until" (to be clear that
meacer 2017/01/31 00:22:47 Done.
196 // first time IsKnownCaptivePortalCert() or SetErrorAssistantProtoForTesting()
197 // is called.
198 std::unique_ptr<std::unordered_set<std::string>> captive_portal_spki_hashes_;
176 }; 199 };
177 200
178 ConfigSingleton::ConfigSingleton() 201 ConfigSingleton::ConfigSingleton()
179 : interstitial_delay_( 202 : interstitial_delay_(
180 base::TimeDelta::FromMilliseconds(kInterstitialDelayInMilliseconds)) { 203 base::TimeDelta::FromMilliseconds(kInterstitialDelayInMilliseconds)) {
181 } 204 }
182 205
183 base::TimeDelta ConfigSingleton::interstitial_delay() const { 206 base::TimeDelta ConfigSingleton::interstitial_delay() const {
184 return interstitial_delay_; 207 return interstitial_delay_;
185 } 208 }
(...skipping 26 matching lines...) Expand all
212 235
213 void ConfigSingleton::SetClockForTesting(base::Clock* clock) { 236 void ConfigSingleton::SetClockForTesting(base::Clock* clock) {
214 testing_clock_ = clock; 237 testing_clock_ = clock;
215 } 238 }
216 239
217 void ConfigSingleton::SetNetworkTimeTrackerForTesting( 240 void ConfigSingleton::SetNetworkTimeTrackerForTesting(
218 network_time::NetworkTimeTracker* tracker) { 241 network_time::NetworkTimeTracker* tracker) {
219 network_time_tracker_ = tracker; 242 network_time_tracker_ = tracker;
220 } 243 }
221 244
245 void ConfigSingleton::SetErrorAssistantProtoForTesting(
246 const chrome_browser_ssl::TLSErrorAssistantConfig& error_assistant_proto) {
247 DCHECK(!captive_portal_spki_hashes_);
248 captive_portal_spki_hashes_ =
249 LoadCaptivePortalCertHashes(error_assistant_proto);
250 }
251
252 bool ConfigSingleton::IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info) {
253 if (!captive_portal_spki_hashes_) {
254 std::string binary_proto;
255 ReadErrorAssistantProtoFromResourceBundle(&binary_proto);
256 chrome_browser_ssl::TLSErrorAssistantConfig proto;
257 proto.ParseFromString(binary_proto);
258 captive_portal_spki_hashes_ = LoadCaptivePortalCertHashes(proto);
259 }
260
261 for (const net::HashValue& hash_value : ssl_info.public_key_hashes) {
262 if (hash_value.tag == net::HASH_VALUE_SHA256 &&
263 captive_portal_spki_hashes_->find(hash_value.ToString()) !=
264 captive_portal_spki_hashes_->end()) {
265 return true;
266 }
267 }
268 return false;
269 }
270
222 static base::LazyInstance<ConfigSingleton>::Leaky g_config = 271 static base::LazyInstance<ConfigSingleton>::Leaky g_config =
223 LAZY_INSTANCE_INITIALIZER; 272 LAZY_INSTANCE_INITIALIZER;
224 273
225 } // namespace 274 } // namespace
226 275
227 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler); 276 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler);
228 DEFINE_WEB_CONTENTS_USER_DATA_KEY(CommonNameMismatchRedirectObserver); 277 DEFINE_WEB_CONTENTS_USER_DATA_KEY(CommonNameMismatchRedirectObserver);
229 278
230 void SSLErrorHandler::HandleSSLError( 279 void SSLErrorHandler::HandleSSLError(
231 content::WebContents* web_contents, 280 content::WebContents* web_contents,
(...skipping 28 matching lines...) Expand all
260 void SSLErrorHandler::SetClockForTesting(base::Clock* testing_clock) { 309 void SSLErrorHandler::SetClockForTesting(base::Clock* testing_clock) {
261 g_config.Pointer()->SetClockForTesting(testing_clock); 310 g_config.Pointer()->SetClockForTesting(testing_clock);
262 } 311 }
263 312
264 // static 313 // static
265 void SSLErrorHandler::SetNetworkTimeTrackerForTesting( 314 void SSLErrorHandler::SetNetworkTimeTrackerForTesting(
266 network_time::NetworkTimeTracker* tracker) { 315 network_time::NetworkTimeTracker* tracker) {
267 g_config.Pointer()->SetNetworkTimeTrackerForTesting(tracker); 316 g_config.Pointer()->SetNetworkTimeTrackerForTesting(tracker);
268 } 317 }
269 318
319 // static
320 void SSLErrorHandler::SetErrorAssistantProtoForTesting(
321 const chrome_browser_ssl::TLSErrorAssistantConfig& config_proto) {
322 g_config.Pointer()->SetErrorAssistantProtoForTesting(config_proto);
323 }
324
325 // static
326 std::string SSLErrorHandler::GetHistogramNameForTesting() {
327 return kHistogram;
328 }
329
270 SSLErrorHandler::SSLErrorHandler( 330 SSLErrorHandler::SSLErrorHandler(
271 content::WebContents* web_contents, 331 content::WebContents* web_contents,
272 int cert_error, 332 int cert_error,
273 const net::SSLInfo& ssl_info, 333 const net::SSLInfo& ssl_info,
274 const GURL& request_url, 334 const GURL& request_url,
275 int options_mask, 335 int options_mask,
276 std::unique_ptr<SSLCertReporter> ssl_cert_reporter, 336 std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
277 const base::Callback<void(content::CertificateRequestResultType)>& callback) 337 const base::Callback<void(content::CertificateRequestResultType)>& callback)
278 : content::WebContentsObserver(web_contents), 338 : content::WebContentsObserver(web_contents),
279 web_contents_(web_contents), 339 web_contents_(web_contents),
(...skipping 11 matching lines...) Expand all
291 351
292 void SSLErrorHandler::StartHandlingError() { 352 void SSLErrorHandler::StartHandlingError() {
293 RecordUMA(HANDLE_ALL); 353 RecordUMA(HANDLE_ALL);
294 354
295 if (ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error_) == 355 if (ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error_) ==
296 ssl_errors::ErrorInfo::CERT_DATE_INVALID) { 356 ssl_errors::ErrorInfo::CERT_DATE_INVALID) {
297 HandleCertDateInvalidError(); 357 HandleCertDateInvalidError();
298 return; 358 return;
299 } 359 }
300 360
361 if (cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID &&
362 g_config.Pointer()->IsKnownCaptivePortalCert(ssl_info_)) {
363 RecordUMA(CAPTIVE_PORTAL_CERT_FOUND);
364 ShowCaptivePortalInterstitial(
365 GURL(captive_portal::CaptivePortalDetector::kDefaultURL));
366 return;
367 }
368
301 std::vector<std::string> dns_names; 369 std::vector<std::string> dns_names;
302 ssl_info_.cert->GetDNSNames(&dns_names); 370 ssl_info_.cert->GetDNSNames(&dns_names);
303 DCHECK(!dns_names.empty()); 371 DCHECK(!dns_names.empty());
304 GURL suggested_url; 372 GURL suggested_url;
305 if (IsSSLCommonNameMismatchHandlingEnabled() && 373 if (IsSSLCommonNameMismatchHandlingEnabled() &&
306 cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID && 374 cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID &&
307 IsErrorOverridable() && GetSuggestedUrl(dns_names, &suggested_url)) { 375 IsErrorOverridable() && GetSuggestedUrl(dns_names, &suggested_url)) {
308 RecordUMA(WWW_MISMATCH_FOUND); 376 RecordUMA(WWW_MISMATCH_FOUND);
309 net::CertStatus extra_cert_errors = 377 net::CertStatus extra_cert_errors =
310 ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID; 378 ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID;
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after
541 network_time::NetworkTimeTracker* tracker = 609 network_time::NetworkTimeTracker* tracker =
542 g_config.Pointer()->network_time_tracker(); 610 g_config.Pointer()->network_time_tracker();
543 ssl_errors::ClockState clock_state = ssl_errors::GetClockState(now, tracker); 611 ssl_errors::ClockState clock_state = ssl_errors::GetClockState(now, tracker);
544 if (clock_state == ssl_errors::CLOCK_STATE_FUTURE || 612 if (clock_state == ssl_errors::CLOCK_STATE_FUTURE ||
545 clock_state == ssl_errors::CLOCK_STATE_PAST) { 613 clock_state == ssl_errors::CLOCK_STATE_PAST) {
546 ShowBadClockInterstitial(now, clock_state); 614 ShowBadClockInterstitial(now, clock_state);
547 return; // |this| is deleted after showing the interstitial. 615 return; // |this| is deleted after showing the interstitial.
548 } 616 }
549 ShowSSLInterstitial(); 617 ShowSSLInterstitial();
550 } 618 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698