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