ssl_blocking_page.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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/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"; 161 162} // namespace 163 164// Note that we always create a navigation entry with SSL errors. 165// No error happening loading a sub-resource triggers an interstitial so far. 166SSLBlockingPage::SSLBlockingPage( 167 content::WebContents* web_contents, 168 int cert_error, 169 const net::SSLInfo& ssl_info, 170 const GURL& request_url, 171 bool overridable, 172 bool strict_enforcement, 173 const base::Callback<void(bool)>& callback) 174 : callback_(callback), 175 web_contents_(web_contents), 176 cert_error_(cert_error), 177 ssl_info_(ssl_info), 178 request_url_(request_url), 179 overridable_(overridable), 180 strict_enforcement_(strict_enforcement) { 181 trialCondition_ = base::FieldTrialList::FindFullName(kStudyName); 182 183 RecordSSLBlockingPageEventStats(SHOW_ALL); 184 if (overridable_ && !strict_enforcement_) 185 RecordSSLBlockingPageEventStats(SHOW_OVERRIDABLE); 186 187 interstitial_page_ = InterstitialPage::Create( 188 web_contents_, true, request_url, this); 189 display_start_time_ = TimeTicks(); 190 interstitial_page_->Show(); 191} 192 193SSLBlockingPage::~SSLBlockingPage() { 194 if (!callback_.is_null()) { 195 RecordSSLBlockingPageTimeStats( 196 false, cert_error_, 197 overridable_ && !strict_enforcement_, display_start_time_, 198 base::TimeTicks::Now()); 199 // The page is closed without the user having chosen what to do, default to 200 // deny. 201 NotifyDenyCertificate(); 202 } 203} 204 205std::string SSLBlockingPage::GetHTMLContents() { 206 // Let's build the html error page. 207 DictionaryValue strings; 208 SSLErrorInfo error_info = SSLErrorInfo::CreateError( 209 SSLErrorInfo::NetErrorToErrorType(cert_error_), ssl_info_.cert, 210 request_url_); 211 212 int resource_id = IDR_SSL_ROAD_BLOCK_HTML; 213 strings.SetString("headLine", error_info.title()); 214 strings.SetString("description", error_info.details()); 215 strings.SetString("moreInfoTitle", 216 l10n_util::GetStringUTF16(IDS_CERT_ERROR_EXTRA_INFO_TITLE)); 217 SetExtraInfo(&strings, error_info.extra_information()); 218 219 strings.SetString("exit", 220 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_EXIT)); 221 222 if (overridable_ && !strict_enforcement_) { 223 strings.SetString("title", 224 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_TITLE)); 225 strings.SetString("proceed", 226 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_PROCEED)); 227 strings.SetString("reasonForNotProceeding", 228 l10n_util::GetStringUTF16( 229 IDS_SSL_BLOCKING_PAGE_SHOULD_NOT_PROCEED)); 230 strings.SetString("errorType", "overridable"); 231 } else { 232 strings.SetString("title", 233 l10n_util::GetStringUTF16(IDS_SSL_ERROR_PAGE_TITLE)); 234 if (strict_enforcement_) { 235 strings.SetString("reasonForNotProceeding", 236 l10n_util::GetStringUTF16( 237 IDS_SSL_ERROR_PAGE_CANNOT_PROCEED)); 238 } else { 239 strings.SetString("reasonForNotProceeding", std::string()); 240 } 241 strings.SetString("errorType", "notoverridable"); 242 } 243 244 strings.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); 245 246 // Set up the Finch trial layouts. 247 strings.SetString("trialType", trialCondition_); 248 if (trialCondition_ == kCondition16Firefox || 249 trialCondition_ == kCondition17FancyFirefox || 250 trialCondition_ == kCondition18NoImages) { 251 strings.SetString("domain", request_url_.host()); 252 std::string font_family = l10n_util::GetStringUTF8(IDS_WEB_FONT_FAMILY); 253#if defined(OS_WIN) 254 if (base::win::GetVersion() < base::win::VERSION_VISTA) { 255 font_family = l10n_util::GetStringUTF8(IDS_WEB_FONT_FAMILY_XP); 256 } 257#endif 258#if defined(TOOLKIT_GTK) 259 font_family = ui::ResourceBundle::GetSharedInstance().GetFont( 260 ui::ResourceBundle::BaseFont).GetFontName() + ", " + font_family; 261#endif 262 strings.SetString("fontfamily", font_family); 263 if (trialCondition_ == kCondition16Firefox || 264 trialCondition_ == kCondition18NoImages) { 265 resource_id = IDR_SSL_FIREFOX_HTML; 266 } else if (trialCondition_ == kCondition17FancyFirefox) { 267 resource_id = IDR_SSL_FANCY_FIREFOX_HTML; 268 } 269 } 270 271 base::StringPiece html( 272 ResourceBundle::GetSharedInstance().GetRawDataResource( 273 resource_id)); 274 275 return webui::GetI18nTemplateHtml(html, &strings); 276} 277 278void SSLBlockingPage::OverrideEntry(NavigationEntry* entry) { 279 int cert_id = content::CertStore::GetInstance()->StoreCert( 280 ssl_info_.cert, web_contents_->GetRenderProcessHost()->GetID()); 281 282 entry->GetSSL().security_style = 283 content::SECURITY_STYLE_AUTHENTICATION_BROKEN; 284 entry->GetSSL().cert_id = cert_id; 285 entry->GetSSL().cert_status = ssl_info_.cert_status; 286 entry->GetSSL().security_bits = ssl_info_.security_bits; 287#if !defined(OS_ANDROID) 288 Browser* browser = chrome::FindBrowserWithWebContents(web_contents_); 289 if (browser) 290 browser->VisibleSSLStateChanged(web_contents_); 291#endif // !defined(OS_ANDROID) 292} 293 294// Matches events defined in ssl_error.html and ssl_roadblock.html. 295void SSLBlockingPage::CommandReceived(const std::string& command) { 296 int cmd = atoi(command.c_str()); 297 if (cmd == CMD_DONT_PROCEED) { 298 interstitial_page_->DontProceed(); 299 } else if (cmd == CMD_PROCEED) { 300 interstitial_page_->Proceed(); 301 } else if (cmd == CMD_FOCUS) { 302 // Start recording the time when the page is first in focus 303 display_start_time_ = base::TimeTicks::Now(); 304 } else if (cmd == CMD_MORE) { 305 RecordSSLBlockingPageEventStats(MORE); 306 } else if (cmd == CMD_SHOW_UNDERSTAND) { 307 // Used in the Finch experiment. 308 RecordSSLBlockingPageEventStats(SHOW_UNDERSTAND); 309 } 310} 311 312void SSLBlockingPage::OverrideRendererPrefs( 313 content::RendererPreferences* prefs) { 314 Profile* profile = Profile::FromBrowserContext( 315 web_contents_->GetBrowserContext()); 316 renderer_preferences_util::UpdateFromSystemSettings(prefs, profile); 317} 318 319void SSLBlockingPage::OnProceed() { 320 RecordSSLBlockingPageTimeStats(true, cert_error_, 321 overridable_ && !strict_enforcement_, display_start_time_, 322 base::TimeTicks::Now()); 323 324 // Accepting the certificate resumes the loading of the page. 325 NotifyAllowCertificate(); 326} 327 328void SSLBlockingPage::OnDontProceed() { 329 RecordSSLBlockingPageTimeStats(false, cert_error_, 330 overridable_ && !strict_enforcement_, display_start_time_, 331 base::TimeTicks::Now()); 332 333 NotifyDenyCertificate(); 334} 335 336void SSLBlockingPage::NotifyDenyCertificate() { 337 // It's possible that callback_ may not exist if the user clicks "Proceed" 338 // followed by pressing the back button before the interstitial is hidden. 339 // In that case the certificate will still be treated as allowed. 340 if (callback_.is_null()) 341 return; 342 343 callback_.Run(false); 344 callback_.Reset(); 345} 346 347void SSLBlockingPage::NotifyAllowCertificate() { 348 DCHECK(!callback_.is_null()); 349 350 callback_.Run(true); 351 callback_.Reset(); 352} 353 354// static 355void SSLBlockingPage::SetExtraInfo( 356 DictionaryValue* strings, 357 const std::vector<string16>& extra_info) { 358 DCHECK_LT(extra_info.size(), 5U); // We allow 5 paragraphs max. 359 const char* keys[5] = { 360 "moreInfo1", "moreInfo2", "moreInfo3", "moreInfo4", "moreInfo5" 361 }; 362 int i; 363 for (i = 0; i < static_cast<int>(extra_info.size()); i++) { 364 strings->SetString(keys[i], extra_info[i]); 365 } 366 for (; i < 5; i++) { 367 strings->SetString(keys[i], std::string()); 368 } 369} 370