ssl_blocking_page.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright (c) 2012 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_blocking_page.h" 6 7#include "base/i18n/rtl.h" 8#include "base/metrics/histogram.h" 9#include "base/strings/string_number_conversions.h" 10#include "base/strings/string_piece.h" 11#include "base/strings/stringprintf.h" 12#include "base/strings/utf_string_conversions.h" 13#include "base/values.h" 14#include "chrome/browser/history/history_service_factory.h" 15#include "chrome/browser/profiles/profile.h" 16#include "chrome/browser/renderer_preferences_util.h" 17#include "chrome/browser/ssl/ssl_error_info.h" 18#include "chrome/browser/ui/browser.h" 19#include "chrome/browser/ui/browser_finder.h" 20#include "content/public/browser/cert_store.h" 21#include "content/public/browser/interstitial_page.h" 22#include "content/public/browser/navigation_controller.h" 23#include "content/public/browser/navigation_entry.h" 24#include "content/public/browser/notification_service.h" 25#include "content/public/browser/notification_types.h" 26#include "content/public/browser/render_process_host.h" 27#include "content/public/browser/render_view_host.h" 28#include "content/public/browser/web_contents.h" 29#include "content/public/common/ssl_status.h" 30#include "grit/app_locale_settings.h" 31#include "grit/browser_resources.h" 32#include "grit/generated_resources.h" 33#include "net/base/hash_value.h" 34#include "net/base/net_errors.h" 35#include "net/base/net_util.h" 36#include "ui/base/l10n/l10n_util.h" 37#include "ui/base/resource/resource_bundle.h" 38#include "ui/base/webui/jstemplate_builder.h" 39 40#if defined(OS_WIN) 41#include "base/win/windows_version.h" 42#endif 43 44using base::ASCIIToUTF16; 45using base::TimeTicks; 46using content::InterstitialPage; 47using content::NavigationController; 48using content::NavigationEntry; 49 50namespace { 51 52// These represent the commands sent by ssl_roadblock.html. 53enum SSLBlockingPageCommands { 54 CMD_DONT_PROCEED, 55 CMD_PROCEED, 56 CMD_MORE, 57 CMD_RELOAD, 58}; 59 60// Events for UMA. 61enum SSLBlockingPageEvent { 62 SHOW_ALL, 63 SHOW_OVERRIDABLE, 64 PROCEED_OVERRIDABLE, 65 PROCEED_NAME, 66 PROCEED_DATE, 67 PROCEED_AUTHORITY, 68 DONT_PROCEED_OVERRIDABLE, 69 DONT_PROCEED_NAME, 70 DONT_PROCEED_DATE, 71 DONT_PROCEED_AUTHORITY, 72 MORE, 73 SHOW_UNDERSTAND, // Used by the summer 2013 Finch trial. Deprecated. 74 SHOW_INTERNAL_HOSTNAME, 75 PROCEED_INTERNAL_HOSTNAME, 76 SHOW_NEW_SITE, 77 PROCEED_NEW_SITE, 78 PROCEED_MANUAL_NONOVERRIDABLE, 79 UNUSED_BLOCKING_PAGE_EVENT, 80}; 81 82void RecordSSLBlockingPageEventStats(SSLBlockingPageEvent event) { 83 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl", 84 event, 85 UNUSED_BLOCKING_PAGE_EVENT); 86} 87 88void RecordSSLBlockingPageDetailedStats( 89 bool proceed, 90 int cert_error, 91 bool overridable, 92 bool internal, 93 int num_visits) { 94 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_type", 95 SSLErrorInfo::NetErrorToErrorType(cert_error), SSLErrorInfo::END_OF_ENUM); 96 if (!overridable) { 97 if (proceed) { 98 RecordSSLBlockingPageEventStats(PROCEED_MANUAL_NONOVERRIDABLE); 99 } 100 // Overridable is false if the user didn't have any option except to turn 101 // back. If that's the case, don't record some of the metrics. 102 return; 103 } 104 if (num_visits == 0) 105 RecordSSLBlockingPageEventStats(SHOW_NEW_SITE); 106 if (proceed) { 107 RecordSSLBlockingPageEventStats(PROCEED_OVERRIDABLE); 108 if (internal) 109 RecordSSLBlockingPageEventStats(PROCEED_INTERNAL_HOSTNAME); 110 if (num_visits == 0) 111 RecordSSLBlockingPageEventStats(PROCEED_NEW_SITE); 112 } else if (!proceed) { 113 RecordSSLBlockingPageEventStats(DONT_PROCEED_OVERRIDABLE); 114 } 115 SSLErrorInfo::ErrorType type = SSLErrorInfo::NetErrorToErrorType(cert_error); 116 switch (type) { 117 case SSLErrorInfo::CERT_COMMON_NAME_INVALID: { 118 if (proceed) 119 RecordSSLBlockingPageEventStats(PROCEED_NAME); 120 else 121 RecordSSLBlockingPageEventStats(DONT_PROCEED_NAME); 122 break; 123 } 124 case SSLErrorInfo::CERT_DATE_INVALID: { 125 if (proceed) 126 RecordSSLBlockingPageEventStats(PROCEED_DATE); 127 else 128 RecordSSLBlockingPageEventStats(DONT_PROCEED_DATE); 129 break; 130 } 131 case SSLErrorInfo::CERT_AUTHORITY_INVALID: { 132 if (proceed) 133 RecordSSLBlockingPageEventStats(PROCEED_AUTHORITY); 134 else 135 RecordSSLBlockingPageEventStats(DONT_PROCEED_AUTHORITY); 136 break; 137 } 138 default: { 139 break; 140 } 141 } 142} 143 144} // namespace 145 146// Note that we always create a navigation entry with SSL errors. 147// No error happening loading a sub-resource triggers an interstitial so far. 148SSLBlockingPage::SSLBlockingPage( 149 content::WebContents* web_contents, 150 int cert_error, 151 const net::SSLInfo& ssl_info, 152 const GURL& request_url, 153 bool overridable, 154 bool strict_enforcement, 155 const base::Callback<void(bool)>& callback) 156 : callback_(callback), 157 web_contents_(web_contents), 158 cert_error_(cert_error), 159 ssl_info_(ssl_info), 160 request_url_(request_url), 161 overridable_(overridable), 162 strict_enforcement_(strict_enforcement), 163 internal_(false), 164 num_visits_(-1) { 165 // For UMA stats. 166 if (net::IsHostnameNonUnique(request_url_.HostNoBrackets())) 167 internal_ = true; 168 RecordSSLBlockingPageEventStats(SHOW_ALL); 169 if (overridable_ && !strict_enforcement_) { 170 RecordSSLBlockingPageEventStats(SHOW_OVERRIDABLE); 171 if (internal_) 172 RecordSSLBlockingPageEventStats(SHOW_INTERNAL_HOSTNAME); 173 HistoryService* history_service = HistoryServiceFactory::GetForProfile( 174 Profile::FromBrowserContext(web_contents->GetBrowserContext()), 175 Profile::EXPLICIT_ACCESS); 176 if (history_service) { 177 history_service->GetVisibleVisitCountToHost( 178 request_url_, 179 &request_consumer_, 180 base::Bind(&SSLBlockingPage::OnGotHistoryCount, 181 base::Unretained(this))); 182 } 183 } 184 185 interstitial_page_ = InterstitialPage::Create( 186 web_contents_, true, request_url, this); 187 interstitial_page_->Show(); 188} 189 190SSLBlockingPage::~SSLBlockingPage() { 191 if (!callback_.is_null()) { 192 RecordSSLBlockingPageDetailedStats(false, 193 cert_error_, 194 overridable_ && !strict_enforcement_, 195 internal_, 196 num_visits_); 197 // The page is closed without the user having chosen what to do, default to 198 // deny. 199 NotifyDenyCertificate(); 200 } 201} 202 203std::string SSLBlockingPage::GetHTMLContents() { 204 base::DictionaryValue strings; 205 int resource_id; 206 if (overridable_ && !strict_enforcement_) { 207 // Let's build the overridable error page. 208 SSLErrorInfo error_info = 209 SSLErrorInfo::CreateError( 210 SSLErrorInfo::NetErrorToErrorType(cert_error_), 211 ssl_info_.cert.get(), 212 request_url_); 213 214 resource_id = IDR_SSL_ROAD_BLOCK_HTML; 215 strings.SetString("headLine", error_info.title()); 216 strings.SetString("description", error_info.details()); 217 strings.SetString("moreInfoTitle", 218 l10n_util::GetStringUTF16(IDS_CERT_ERROR_EXTRA_INFO_TITLE)); 219 SetExtraInfo(&strings, error_info.extra_information()); 220 221 strings.SetString( 222 "exit", l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_PAGE_EXIT)); 223 strings.SetString( 224 "title", l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_PAGE_TITLE)); 225 strings.SetString( 226 "proceed", l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_PAGE_PROCEED)); 227 strings.SetString( 228 "reasonForNotProceeding", l10n_util::GetStringUTF16( 229 IDS_SSL_OVERRIDABLE_PAGE_SHOULD_NOT_PROCEED)); 230 strings.SetString("errorType", "overridable"); 231 strings.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); 232 } else { 233 // Let's build the blocking error page. 234 resource_id = IDR_SSL_BLOCKING_HTML; 235 236 // Strings that are not dependent on the URL. 237 strings.SetString( 238 "title", l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_TITLE)); 239 strings.SetString( 240 "reloadMsg", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_RELOAD)); 241 strings.SetString( 242 "more", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_MORE)); 243 strings.SetString( 244 "less", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_LESS)); 245 strings.SetString( 246 "moreTitle", 247 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_MORE_TITLE)); 248 strings.SetString( 249 "techTitle", 250 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_TECH_TITLE)); 251 252 // Strings that are dependent on the URL. 253 base::string16 url(ASCIIToUTF16(request_url_.host())); 254 bool rtl = base::i18n::IsRTL(); 255 strings.SetString("textDirection", rtl ? "rtl" : "ltr"); 256 if (rtl) 257 base::i18n::WrapStringWithLTRFormatting(&url); 258 strings.SetString( 259 "headline", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_HEADLINE, 260 url.c_str())); 261 strings.SetString( 262 "message", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_BODY_TEXT, 263 url.c_str())); 264 strings.SetString( 265 "moreMessage", 266 l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_MORE_TEXT, 267 url.c_str())); 268 strings.SetString("reloadUrl", request_url_.spec()); 269 270 // Strings that are dependent on the error type. 271 SSLErrorInfo::ErrorType type = 272 SSLErrorInfo::NetErrorToErrorType(cert_error_); 273 base::string16 errorType; 274 if (type == SSLErrorInfo::CERT_REVOKED) { 275 errorType = base::string16(ASCIIToUTF16("Key revocation")); 276 strings.SetString( 277 "failure", 278 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_REVOKED)); 279 } else if (type == SSLErrorInfo::CERT_INVALID) { 280 errorType = base::string16(ASCIIToUTF16("Malformed certificate")); 281 strings.SetString( 282 "failure", 283 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_FORMATTED)); 284 } else if (type == SSLErrorInfo::CERT_PINNED_KEY_MISSING) { 285 errorType = base::string16(ASCIIToUTF16("Certificate pinning failure")); 286 strings.SetString( 287 "failure", 288 l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_PINNING, 289 url.c_str())); 290 } else if (type == SSLErrorInfo::CERT_WEAK_KEY_DH) { 291 errorType = base::string16(ASCIIToUTF16("Weak DH public key")); 292 strings.SetString( 293 "failure", 294 l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_WEAK_DH, 295 url.c_str())); 296 } else { 297 // HSTS failure. 298 errorType = base::string16(ASCIIToUTF16("HSTS failure")); 299 strings.SetString( 300 "failure", 301 l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_HSTS, url.c_str())); 302 } 303 if (rtl) 304 base::i18n::WrapStringWithLTRFormatting(&errorType); 305 strings.SetString( 306 "errorType", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_ERROR, 307 errorType.c_str())); 308 309 // Strings that display the invalid cert. 310 base::string16 subject( 311 ASCIIToUTF16(ssl_info_.cert->subject().GetDisplayName())); 312 base::string16 issuer( 313 ASCIIToUTF16(ssl_info_.cert->issuer().GetDisplayName())); 314 std::string hashes; 315 for (std::vector<net::HashValue>::iterator it = 316 ssl_info_.public_key_hashes.begin(); 317 it != ssl_info_.public_key_hashes.end(); 318 ++it) { 319 base::StringAppendF(&hashes, "%s ", it->ToString().c_str()); 320 } 321 base::string16 fingerprint(ASCIIToUTF16(hashes)); 322 if (rtl) { 323 // These are always going to be LTR. 324 base::i18n::WrapStringWithLTRFormatting(&subject); 325 base::i18n::WrapStringWithLTRFormatting(&issuer); 326 base::i18n::WrapStringWithLTRFormatting(&fingerprint); 327 } 328 strings.SetString( 329 "subject", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_SUBJECT, 330 subject.c_str())); 331 strings.SetString( 332 "issuer", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_ISSUER, 333 issuer.c_str())); 334 strings.SetString( 335 "fingerprint", 336 l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_HASHES, 337 fingerprint.c_str())); 338 } 339 340 base::StringPiece html( 341 ResourceBundle::GetSharedInstance().GetRawDataResource( 342 resource_id)); 343 return webui::GetI18nTemplateHtml(html, &strings); 344} 345 346void SSLBlockingPage::OverrideEntry(NavigationEntry* entry) { 347 int cert_id = content::CertStore::GetInstance()->StoreCert( 348 ssl_info_.cert.get(), web_contents_->GetRenderProcessHost()->GetID()); 349 350 entry->GetSSL().security_style = 351 content::SECURITY_STYLE_AUTHENTICATION_BROKEN; 352 entry->GetSSL().cert_id = cert_id; 353 entry->GetSSL().cert_status = ssl_info_.cert_status; 354 entry->GetSSL().security_bits = ssl_info_.security_bits; 355#if !defined(OS_ANDROID) 356 Browser* browser = chrome::FindBrowserWithWebContents(web_contents_); 357 if (browser) 358 browser->VisibleSSLStateChanged(web_contents_); 359#endif // !defined(OS_ANDROID) 360} 361 362// Matches events defined in ssl_error.html and ssl_roadblock.html. 363void SSLBlockingPage::CommandReceived(const std::string& command) { 364 int cmd = atoi(command.c_str()); 365 if (cmd == CMD_DONT_PROCEED) { 366 interstitial_page_->DontProceed(); 367 } else if (cmd == CMD_PROCEED) { 368 interstitial_page_->Proceed(); 369 } else if (cmd == CMD_MORE) { 370 RecordSSLBlockingPageEventStats(MORE); 371 } else if (cmd == CMD_RELOAD) { 372 // The interstitial can't refresh itself. 373 content::NavigationController* controller = &web_contents_->GetController(); 374 controller->Reload(true); 375 } 376} 377 378void SSLBlockingPage::OverrideRendererPrefs( 379 content::RendererPreferences* prefs) { 380 Profile* profile = Profile::FromBrowserContext( 381 web_contents_->GetBrowserContext()); 382 renderer_preferences_util::UpdateFromSystemSettings(prefs, profile); 383} 384 385void SSLBlockingPage::OnProceed() { 386 RecordSSLBlockingPageDetailedStats(true, 387 cert_error_, 388 overridable_ && !strict_enforcement_, 389 internal_, 390 num_visits_); 391 // Accepting the certificate resumes the loading of the page. 392 NotifyAllowCertificate(); 393} 394 395void SSLBlockingPage::OnDontProceed() { 396 RecordSSLBlockingPageDetailedStats(false, 397 cert_error_, 398 overridable_ && !strict_enforcement_, 399 internal_, 400 num_visits_); 401 NotifyDenyCertificate(); 402} 403 404void SSLBlockingPage::NotifyDenyCertificate() { 405 // It's possible that callback_ may not exist if the user clicks "Proceed" 406 // followed by pressing the back button before the interstitial is hidden. 407 // In that case the certificate will still be treated as allowed. 408 if (callback_.is_null()) 409 return; 410 411 callback_.Run(false); 412 callback_.Reset(); 413} 414 415void SSLBlockingPage::NotifyAllowCertificate() { 416 DCHECK(!callback_.is_null()); 417 418 callback_.Run(true); 419 callback_.Reset(); 420} 421 422// static 423void SSLBlockingPage::SetExtraInfo( 424 base::DictionaryValue* strings, 425 const std::vector<base::string16>& extra_info) { 426 DCHECK_LT(extra_info.size(), 5U); // We allow 5 paragraphs max. 427 const char* keys[5] = { 428 "moreInfo1", "moreInfo2", "moreInfo3", "moreInfo4", "moreInfo5" 429 }; 430 int i; 431 for (i = 0; i < static_cast<int>(extra_info.size()); i++) { 432 strings->SetString(keys[i], extra_info[i]); 433 } 434 for (; i < 5; i++) { 435 strings->SetString(keys[i], std::string()); 436 } 437} 438 439void SSLBlockingPage::OnGotHistoryCount(HistoryService::Handle handle, 440 bool success, 441 int num_visits, 442 base::Time first_visit) { 443 num_visits_ = num_visits; 444} 445