ssl_blocking_page.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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
44using base::TimeTicks;
45using content::InterstitialPage;
46using content::NavigationController;
47using content::NavigationEntry;
48
49namespace {
50
51// These represent the commands sent by ssl_roadblock.html.
52enum SSLBlockingPageCommands {
53  CMD_DONT_PROCEED,
54  CMD_PROCEED,
55  CMD_MORE,
56  CMD_RELOAD,
57};
58
59// Events for UMA.
60enum 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
80void RecordSSLBlockingPageEventStats(SSLBlockingPageEvent event) {
81  UMA_HISTOGRAM_ENUMERATION("interstitial.ssl",
82                            event,
83                            UNUSED_BLOCKING_PAGE_EVENT);
84}
85
86void 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.
143SSLBlockingPage::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
185SSLBlockingPage::~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
198std::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    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    string16 errorType;
269    if (type == SSLErrorInfo::CERT_REVOKED) {
270      errorType = 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 = 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 = 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 = 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 = 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    string16 subject(ASCIIToUTF16(ssl_info_.cert->subject().GetDisplayName()));
306    string16 issuer(ASCIIToUTF16(ssl_info_.cert->issuer().GetDisplayName()));
307    std::string hashes;
308    for (std::vector<net::HashValue>::iterator it =
309            ssl_info_.public_key_hashes.begin();
310         it != ssl_info_.public_key_hashes.end();
311         ++it) {
312      base::StringAppendF(&hashes, "%s ", it->ToString().c_str());
313    }
314    string16 fingerprint(ASCIIToUTF16(hashes));
315    if (rtl) {
316      // These are always going to be LTR.
317      base::i18n::WrapStringWithLTRFormatting(&subject);
318      base::i18n::WrapStringWithLTRFormatting(&issuer);
319      base::i18n::WrapStringWithLTRFormatting(&fingerprint);
320    }
321    strings.SetString(
322        "subject", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_SUBJECT,
323                                              subject.c_str()));
324    strings.SetString(
325        "issuer", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_ISSUER,
326                                             issuer.c_str()));
327    strings.SetString(
328        "fingerprint",
329        l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_HASHES,
330                                   fingerprint.c_str()));
331  }
332
333  base::StringPiece html(
334      ResourceBundle::GetSharedInstance().GetRawDataResource(
335          resource_id));
336  return webui::GetI18nTemplateHtml(html, &strings);
337}
338
339void SSLBlockingPage::OverrideEntry(NavigationEntry* entry) {
340  int cert_id = content::CertStore::GetInstance()->StoreCert(
341      ssl_info_.cert.get(), web_contents_->GetRenderProcessHost()->GetID());
342
343  entry->GetSSL().security_style =
344      content::SECURITY_STYLE_AUTHENTICATION_BROKEN;
345  entry->GetSSL().cert_id = cert_id;
346  entry->GetSSL().cert_status = ssl_info_.cert_status;
347  entry->GetSSL().security_bits = ssl_info_.security_bits;
348#if !defined(OS_ANDROID)
349  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
350  if (browser)
351    browser->VisibleSSLStateChanged(web_contents_);
352#endif  // !defined(OS_ANDROID)
353}
354
355// Matches events defined in ssl_error.html and ssl_roadblock.html.
356void SSLBlockingPage::CommandReceived(const std::string& command) {
357  int cmd = atoi(command.c_str());
358  if (cmd == CMD_DONT_PROCEED) {
359    interstitial_page_->DontProceed();
360  } else if (cmd == CMD_PROCEED) {
361    interstitial_page_->Proceed();
362  } else if (cmd == CMD_MORE) {
363    RecordSSLBlockingPageEventStats(MORE);
364  } else if (cmd == CMD_RELOAD) {
365    // The interstitial can't refresh itself.
366    content::NavigationController* controller = &web_contents_->GetController();
367    controller->Reload(true);
368  }
369}
370
371void SSLBlockingPage::OverrideRendererPrefs(
372      content::RendererPreferences* prefs) {
373  Profile* profile = Profile::FromBrowserContext(
374      web_contents_->GetBrowserContext());
375  renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
376}
377
378void SSLBlockingPage::OnProceed() {
379  RecordSSLBlockingPageDetailedStats(true,
380                                     cert_error_,
381                                     overridable_ && !strict_enforcement_,
382                                     internal_,
383                                     num_visits_);
384  // Accepting the certificate resumes the loading of the page.
385  NotifyAllowCertificate();
386}
387
388void SSLBlockingPage::OnDontProceed() {
389  RecordSSLBlockingPageDetailedStats(false,
390                                     cert_error_,
391                                     overridable_ && !strict_enforcement_,
392                                     internal_,
393                                     num_visits_);
394  NotifyDenyCertificate();
395}
396
397void SSLBlockingPage::NotifyDenyCertificate() {
398  // It's possible that callback_ may not exist if the user clicks "Proceed"
399  // followed by pressing the back button before the interstitial is hidden.
400  // In that case the certificate will still be treated as allowed.
401  if (callback_.is_null())
402    return;
403
404  callback_.Run(false);
405  callback_.Reset();
406}
407
408void SSLBlockingPage::NotifyAllowCertificate() {
409  DCHECK(!callback_.is_null());
410
411  callback_.Run(true);
412  callback_.Reset();
413}
414
415// static
416void SSLBlockingPage::SetExtraInfo(
417    DictionaryValue* strings,
418    const std::vector<string16>& extra_info) {
419  DCHECK_LT(extra_info.size(), 5U);  // We allow 5 paragraphs max.
420  const char* keys[5] = {
421      "moreInfo1", "moreInfo2", "moreInfo3", "moreInfo4", "moreInfo5"
422  };
423  int i;
424  for (i = 0; i < static_cast<int>(extra_info.size()); i++) {
425    strings->SetString(keys[i], extra_info[i]);
426  }
427  for (; i < 5; i++) {
428    strings->SetString(keys[i], std::string());
429  }
430}
431
432void SSLBlockingPage::OnGotHistoryCount(HistoryService::Handle handle,
433                                        bool success,
434                                        int num_visits,
435                                        base::Time first_visit) {
436  num_visits_ = num_visits;
437}
438