ssl_blocking_page.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/field_trial.h" 9#include "base/metrics/histogram.h" 10#include "base/strings/string_piece.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/values.h" 13#include "chrome/browser/profiles/profile.h" 14#include "chrome/browser/renderer_preferences_util.h" 15#include "chrome/browser/ssl/ssl_error_info.h" 16#include "chrome/browser/ui/browser.h" 17#include "chrome/browser/ui/browser_finder.h" 18#include "content/public/browser/cert_store.h" 19#include "content/public/browser/interstitial_page.h" 20#include "content/public/browser/navigation_controller.h" 21#include "content/public/browser/navigation_entry.h" 22#include "content/public/browser/notification_service.h" 23#include "content/public/browser/notification_types.h" 24#include "content/public/browser/render_process_host.h" 25#include "content/public/browser/render_view_host.h" 26#include "content/public/browser/web_contents.h" 27#include "content/public/common/ssl_status.h" 28#include "grit/app_locale_settings.h" 29#include "grit/browser_resources.h" 30#include "grit/generated_resources.h" 31#include "net/base/net_errors.h" 32#include "ui/base/l10n/l10n_util.h" 33#include "ui/base/resource/resource_bundle.h" 34#include "ui/webui/jstemplate_builder.h" 35 36#if defined(OS_WIN) 37#include "base/win/windows_version.h" 38#endif 39 40using base::TimeDelta; 41using base::TimeTicks; 42using content::InterstitialPage; 43using content::NavigationController; 44using content::NavigationEntry; 45 46#define HISTOGRAM_INTERSTITIAL_SMALL_TIME(name, sample) \ 47 UMA_HISTOGRAM_CUSTOM_TIMES( \ 48 name, \ 49 sample, \ 50 base::TimeDelta::FromMilliseconds(400), \ 51 base::TimeDelta::FromMinutes(15), 75); 52 53#define HISTOGRAM_INTERSTITIAL_LARGE_TIME(name, sample) \ 54 UMA_HISTOGRAM_CUSTOM_TIMES( \ 55 name, \ 56 sample, \ 57 base::TimeDelta::FromMilliseconds(400), \ 58 base::TimeDelta::FromMinutes(20), 50); 59 60namespace { 61 62// These represent the commands sent by ssl_roadblock.html. 63enum SSLBlockingPageCommands { 64 CMD_DONT_PROCEED, 65 CMD_PROCEED, 66 CMD_FOCUS, 67 CMD_MORE, 68 CMD_SHOW_UNDERSTAND, // Used by the Finch trial. 69}; 70 71// Events for UMA. 72enum SSLBlockingPageEvent { 73 SHOW_ALL, 74 SHOW_OVERRIDABLE, 75 PROCEED_OVERRIDABLE, 76 PROCEED_NAME, 77 PROCEED_DATE, 78 PROCEED_AUTHORITY, 79 DONT_PROCEED_OVERRIDABLE, 80 DONT_PROCEED_NAME, 81 DONT_PROCEED_DATE, 82 DONT_PROCEED_AUTHORITY, 83 MORE, 84 SHOW_UNDERSTAND, 85 UNUSED_BLOCKING_PAGE_EVENT, 86}; 87 88void RecordSSLBlockingPageEventStats(SSLBlockingPageEvent event) { 89 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl", 90 event, 91 UNUSED_BLOCKING_PAGE_EVENT); 92} 93 94void RecordSSLBlockingPageTimeStats( 95 bool proceed, 96 int cert_error, 97 bool overridable, 98 const base::TimeTicks& start_time, 99 const base::TimeTicks& end_time) { 100 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_type", 101 SSLErrorInfo::NetErrorToErrorType(cert_error), SSLErrorInfo::END_OF_ENUM); 102 if (start_time.is_null() || !overridable) { 103 // A null start time will occur if the page never came into focus and the 104 // user quit without seeing it. If so, we don't record the time. 105 // The user might not have an option except to turn back; that happens 106 // if overridable is true. If so, the time/outcome isn't meaningful. 107 return; 108 } 109 base::TimeDelta delta = end_time - start_time; 110 if (proceed) { 111 RecordSSLBlockingPageEventStats(PROCEED_OVERRIDABLE); 112 HISTOGRAM_INTERSTITIAL_LARGE_TIME("interstitial.ssl_accept_time", delta); 113 } else if (!proceed) { 114 RecordSSLBlockingPageEventStats(DONT_PROCEED_OVERRIDABLE); 115 HISTOGRAM_INTERSTITIAL_LARGE_TIME("interstitial.ssl_reject_time", delta); 116 } 117 SSLErrorInfo::ErrorType type = SSLErrorInfo::NetErrorToErrorType(cert_error); 118 switch (type) { 119 case SSLErrorInfo::CERT_COMMON_NAME_INVALID: { 120 HISTOGRAM_INTERSTITIAL_SMALL_TIME( 121 "interstitial.common_name_invalid_time", 122 delta); 123 if (proceed) 124 RecordSSLBlockingPageEventStats(PROCEED_NAME); 125 else 126 RecordSSLBlockingPageEventStats(DONT_PROCEED_NAME); 127 break; 128 } 129 case SSLErrorInfo::CERT_DATE_INVALID: { 130 HISTOGRAM_INTERSTITIAL_SMALL_TIME( 131 "interstitial.date_invalid_time", 132 delta); 133 if (proceed) 134 RecordSSLBlockingPageEventStats(PROCEED_DATE); 135 else 136 RecordSSLBlockingPageEventStats(DONT_PROCEED_DATE); 137 break; 138 } 139 case SSLErrorInfo::CERT_AUTHORITY_INVALID: { 140 HISTOGRAM_INTERSTITIAL_SMALL_TIME( 141 "interstitial.authority_invalid_time", 142 delta); 143 if (proceed) 144 RecordSSLBlockingPageEventStats(PROCEED_AUTHORITY); 145 else 146 RecordSSLBlockingPageEventStats(DONT_PROCEED_AUTHORITY); 147 break; 148 } 149 default: { 150 break; 151 } 152 } 153} 154 155// These are the constants for the Finch experiment. 156const char kStudyName[] = "InterstitialSSL517"; 157const char kCondition15Control[] = "Condition15SSLControl"; 158const char kCondition16Firefox[] = "Condition16SSLFirefox"; 159const char kCondition17FancyFirefox[] = "Condition17SSLFancyFirefox"; 160const char kCondition18NoImages[] = "Condition18SSLNoImages"; 161const char kCondition19Policeman[] = "Condition19SSLPoliceman"; 162const char kCondition20Stoplight[] = "Condition20SSLStoplight"; 163const char kCondition21Badguy[] = "Condition21SSLBadguy"; 164 165} // namespace 166 167// Note that we always create a navigation entry with SSL errors. 168// No error happening loading a sub-resource triggers an interstitial so far. 169SSLBlockingPage::SSLBlockingPage( 170 content::WebContents* web_contents, 171 int cert_error, 172 const net::SSLInfo& ssl_info, 173 const GURL& request_url, 174 bool overridable, 175 bool strict_enforcement, 176 const base::Callback<void(bool)>& callback) 177 : callback_(callback), 178 web_contents_(web_contents), 179 cert_error_(cert_error), 180 ssl_info_(ssl_info), 181 request_url_(request_url), 182 overridable_(overridable), 183 strict_enforcement_(strict_enforcement) { 184 trialCondition_ = base::FieldTrialList::FindFullName(kStudyName); 185 186 RecordSSLBlockingPageEventStats(SHOW_ALL); 187 if (overridable_ && !strict_enforcement_) 188 RecordSSLBlockingPageEventStats(SHOW_OVERRIDABLE); 189 190 interstitial_page_ = InterstitialPage::Create( 191 web_contents_, true, request_url, this); 192 display_start_time_ = TimeTicks(); 193 interstitial_page_->Show(); 194} 195 196SSLBlockingPage::~SSLBlockingPage() { 197 if (!callback_.is_null()) { 198 RecordSSLBlockingPageTimeStats( 199 false, cert_error_, 200 overridable_ && !strict_enforcement_, display_start_time_, 201 base::TimeTicks::Now()); 202 // The page is closed without the user having chosen what to do, default to 203 // deny. 204 NotifyDenyCertificate(); 205 } 206} 207 208std::string SSLBlockingPage::GetHTMLContents() { 209 // Let's build the html error page. 210 DictionaryValue strings; 211 SSLErrorInfo error_info = 212 SSLErrorInfo::CreateError(SSLErrorInfo::NetErrorToErrorType(cert_error_), 213 ssl_info_.cert.get(), 214 request_url_); 215 216 int resource_id = IDR_SSL_ROAD_BLOCK_HTML; 217 strings.SetString("headLine", error_info.title()); 218 strings.SetString("description", error_info.details()); 219 strings.SetString("moreInfoTitle", 220 l10n_util::GetStringUTF16(IDS_CERT_ERROR_EXTRA_INFO_TITLE)); 221 SetExtraInfo(&strings, error_info.extra_information()); 222 223 strings.SetString("exit", 224 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_EXIT)); 225 226 if (overridable_ && !strict_enforcement_) { 227 strings.SetString("title", 228 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_TITLE)); 229 strings.SetString("proceed", 230 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_PROCEED)); 231 strings.SetString("reasonForNotProceeding", 232 l10n_util::GetStringUTF16( 233 IDS_SSL_BLOCKING_PAGE_SHOULD_NOT_PROCEED)); 234 strings.SetString("errorType", "overridable"); 235 } else { 236 strings.SetString("title", 237 l10n_util::GetStringUTF16(IDS_SSL_ERROR_PAGE_TITLE)); 238 if (strict_enforcement_) { 239 strings.SetString("reasonForNotProceeding", 240 l10n_util::GetStringUTF16( 241 IDS_SSL_ERROR_PAGE_CANNOT_PROCEED)); 242 } else { 243 strings.SetString("reasonForNotProceeding", std::string()); 244 } 245 strings.SetString("errorType", "notoverridable"); 246 } 247 248 strings.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); 249 250 // Set up the Finch trial layouts. 251 strings.SetString("trialType", trialCondition_); 252 if (trialCondition_ == kCondition16Firefox || 253 trialCondition_ == kCondition17FancyFirefox || 254 trialCondition_ == kCondition18NoImages) { 255 strings.SetString("domain", request_url_.host()); 256 std::string font_family = l10n_util::GetStringUTF8(IDS_WEB_FONT_FAMILY); 257#if defined(OS_WIN) 258 if (base::win::GetVersion() < base::win::VERSION_VISTA) { 259 font_family = l10n_util::GetStringUTF8(IDS_WEB_FONT_FAMILY_XP); 260 } 261#endif 262#if defined(TOOLKIT_GTK) 263 font_family = ui::ResourceBundle::GetSharedInstance().GetFont( 264 ui::ResourceBundle::BaseFont).GetFontName() + ", " + font_family; 265#endif 266 strings.SetString("fontfamily", font_family); 267 if (trialCondition_ == kCondition16Firefox || 268 trialCondition_ == kCondition18NoImages) { 269 resource_id = IDR_SSL_FIREFOX_HTML; 270 } else if (trialCondition_ == kCondition17FancyFirefox) { 271 resource_id = IDR_SSL_FANCY_FIREFOX_HTML; 272 } 273 } 274 275 base::StringPiece html( 276 ResourceBundle::GetSharedInstance().GetRawDataResource( 277 resource_id)); 278 279 return webui::GetI18nTemplateHtml(html, &strings); 280} 281 282void SSLBlockingPage::OverrideEntry(NavigationEntry* entry) { 283 int cert_id = content::CertStore::GetInstance()->StoreCert( 284 ssl_info_.cert.get(), web_contents_->GetRenderProcessHost()->GetID()); 285 286 entry->GetSSL().security_style = 287 content::SECURITY_STYLE_AUTHENTICATION_BROKEN; 288 entry->GetSSL().cert_id = cert_id; 289 entry->GetSSL().cert_status = ssl_info_.cert_status; 290 entry->GetSSL().security_bits = ssl_info_.security_bits; 291#if !defined(OS_ANDROID) 292 Browser* browser = chrome::FindBrowserWithWebContents(web_contents_); 293 if (browser) 294 browser->VisibleSSLStateChanged(web_contents_); 295#endif // !defined(OS_ANDROID) 296} 297 298// Matches events defined in ssl_error.html and ssl_roadblock.html. 299void SSLBlockingPage::CommandReceived(const std::string& command) { 300 int cmd = atoi(command.c_str()); 301 if (cmd == CMD_DONT_PROCEED) { 302 interstitial_page_->DontProceed(); 303 } else if (cmd == CMD_PROCEED) { 304 interstitial_page_->Proceed(); 305 } else if (cmd == CMD_FOCUS) { 306 // Start recording the time when the page is first in focus 307 display_start_time_ = base::TimeTicks::Now(); 308 } else if (cmd == CMD_MORE) { 309 RecordSSLBlockingPageEventStats(MORE); 310 } else if (cmd == CMD_SHOW_UNDERSTAND) { 311 // Used in the Finch experiment. 312 RecordSSLBlockingPageEventStats(SHOW_UNDERSTAND); 313 } 314} 315 316void SSLBlockingPage::OverrideRendererPrefs( 317 content::RendererPreferences* prefs) { 318 Profile* profile = Profile::FromBrowserContext( 319 web_contents_->GetBrowserContext()); 320 renderer_preferences_util::UpdateFromSystemSettings(prefs, profile); 321} 322 323void SSLBlockingPage::OnProceed() { 324 RecordSSLBlockingPageTimeStats(true, cert_error_, 325 overridable_ && !strict_enforcement_, display_start_time_, 326 base::TimeTicks::Now()); 327 328 // Accepting the certificate resumes the loading of the page. 329 NotifyAllowCertificate(); 330} 331 332void SSLBlockingPage::OnDontProceed() { 333 RecordSSLBlockingPageTimeStats(false, cert_error_, 334 overridable_ && !strict_enforcement_, display_start_time_, 335 base::TimeTicks::Now()); 336 337 NotifyDenyCertificate(); 338} 339 340void SSLBlockingPage::NotifyDenyCertificate() { 341 // It's possible that callback_ may not exist if the user clicks "Proceed" 342 // followed by pressing the back button before the interstitial is hidden. 343 // In that case the certificate will still be treated as allowed. 344 if (callback_.is_null()) 345 return; 346 347 callback_.Run(false); 348 callback_.Reset(); 349} 350 351void SSLBlockingPage::NotifyAllowCertificate() { 352 DCHECK(!callback_.is_null()); 353 354 callback_.Run(true); 355 callback_.Reset(); 356} 357 358// static 359void SSLBlockingPage::SetExtraInfo( 360 DictionaryValue* strings, 361 const std::vector<string16>& extra_info) { 362 DCHECK_LT(extra_info.size(), 5U); // We allow 5 paragraphs max. 363 const char* keys[5] = { 364 "moreInfo1", "moreInfo2", "moreInfo3", "moreInfo4", "moreInfo5" 365 }; 366 int i; 367 for (i = 0; i < static_cast<int>(extra_info.size()); i++) { 368 strings->SetString(keys[i], extra_info[i]); 369 } 370 for (; i < 5; i++) { 371 strings->SetString(keys[i], std::string()); 372 } 373} 374