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