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