Home | History | Annotate | Download | only in ssl
      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