ssl_blocking_page.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/base/webui/jstemplate_builder.h"
37
38#if defined(OS_WIN)
39#include "base/win/windows_version.h"
40#endif
41
42using base::TimeTicks;
43using content::InterstitialPage;
44using content::NavigationController;
45using content::NavigationEntry;
46
47namespace {
48
49// These represent the commands sent by ssl_roadblock.html.
50enum SSLBlockingPageCommands {
51  CMD_DONT_PROCEED,
52  CMD_PROCEED,
53  CMD_FOCUS,
54  CMD_MORE
55};
56
57// Events for UMA.
58enum SSLBlockingPageEvent {
59  SHOW_ALL,
60  SHOW_OVERRIDABLE,
61  PROCEED_OVERRIDABLE,
62  PROCEED_NAME,
63  PROCEED_DATE,
64  PROCEED_AUTHORITY,
65  DONT_PROCEED_OVERRIDABLE,
66  DONT_PROCEED_NAME,
67  DONT_PROCEED_DATE,
68  DONT_PROCEED_AUTHORITY,
69  MORE,
70  SHOW_UNDERSTAND,  // Used by the summer 2013 Finch trial. Deprecated.
71  SHOW_INTERNAL_HOSTNAME,
72  PROCEED_INTERNAL_HOSTNAME,
73  SHOW_NEW_SITE,
74  PROCEED_NEW_SITE,
75  UNUSED_BLOCKING_PAGE_EVENT,
76};
77
78void RecordSSLBlockingPageEventStats(SSLBlockingPageEvent event) {
79  UMA_HISTOGRAM_ENUMERATION("interstitial.ssl",
80                            event,
81                            UNUSED_BLOCKING_PAGE_EVENT);
82}
83
84void RecordSSLBlockingPageDetailedStats(
85    bool proceed,
86    int cert_error,
87    bool overridable,
88    bool internal,
89    const base::TimeTicks& start_time,
90    int num_visits) {
91  UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_type",
92     SSLErrorInfo::NetErrorToErrorType(cert_error), SSLErrorInfo::END_OF_ENUM);
93  if (start_time.is_null() || !overridable) {
94    // A null start time will occur if the page never came into focus.
95    // Overridable is false if the user didn't have any option except to turn
96    // back. In either case, we don't want to record some of our 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  display_start_time_ = TimeTicks();
183  interstitial_page_->Show();
184}
185
186SSLBlockingPage::~SSLBlockingPage() {
187  if (!callback_.is_null()) {
188    RecordSSLBlockingPageDetailedStats(false,
189                                       cert_error_,
190                                       overridable_ && !strict_enforcement_,
191                                       internal_,
192                                       display_start_time_,
193                                       num_visits_);
194    // The page is closed without the user having chosen what to do, default to
195    // deny.
196    NotifyDenyCertificate();
197  }
198}
199
200std::string SSLBlockingPage::GetHTMLContents() {
201  // Let's build the html error page.
202  DictionaryValue strings;
203  SSLErrorInfo error_info =
204      SSLErrorInfo::CreateError(SSLErrorInfo::NetErrorToErrorType(cert_error_),
205                                ssl_info_.cert.get(),
206                                request_url_);
207
208  int resource_id = IDR_SSL_ROAD_BLOCK_HTML;
209  strings.SetString("headLine", error_info.title());
210  strings.SetString("description", error_info.details());
211  strings.SetString("moreInfoTitle",
212      l10n_util::GetStringUTF16(IDS_CERT_ERROR_EXTRA_INFO_TITLE));
213  SetExtraInfo(&strings, error_info.extra_information());
214
215  strings.SetString("exit",
216                    l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_EXIT));
217
218  if (overridable_ && !strict_enforcement_) {
219    strings.SetString("title",
220                      l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_TITLE));
221    strings.SetString("proceed",
222                      l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_PROCEED));
223    strings.SetString("reasonForNotProceeding",
224                      l10n_util::GetStringUTF16(
225                          IDS_SSL_BLOCKING_PAGE_SHOULD_NOT_PROCEED));
226    strings.SetString("errorType", "overridable");
227  } else {
228    strings.SetString("title",
229                      l10n_util::GetStringUTF16(IDS_SSL_ERROR_PAGE_TITLE));
230    if (strict_enforcement_) {
231      strings.SetString("reasonForNotProceeding",
232                        l10n_util::GetStringUTF16(
233                            IDS_SSL_ERROR_PAGE_CANNOT_PROCEED));
234    } else {
235      strings.SetString("reasonForNotProceeding", std::string());
236    }
237    strings.SetString("errorType", "notoverridable");
238  }
239
240  strings.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr");
241
242  base::StringPiece html(
243      ResourceBundle::GetSharedInstance().GetRawDataResource(
244          resource_id));
245
246  return webui::GetI18nTemplateHtml(html, &strings);
247}
248
249void SSLBlockingPage::OverrideEntry(NavigationEntry* entry) {
250  int cert_id = content::CertStore::GetInstance()->StoreCert(
251      ssl_info_.cert.get(), web_contents_->GetRenderProcessHost()->GetID());
252
253  entry->GetSSL().security_style =
254      content::SECURITY_STYLE_AUTHENTICATION_BROKEN;
255  entry->GetSSL().cert_id = cert_id;
256  entry->GetSSL().cert_status = ssl_info_.cert_status;
257  entry->GetSSL().security_bits = ssl_info_.security_bits;
258#if !defined(OS_ANDROID)
259  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
260  if (browser)
261    browser->VisibleSSLStateChanged(web_contents_);
262#endif  // !defined(OS_ANDROID)
263}
264
265// Matches events defined in ssl_error.html and ssl_roadblock.html.
266void SSLBlockingPage::CommandReceived(const std::string& command) {
267  int cmd = atoi(command.c_str());
268  if (cmd == CMD_DONT_PROCEED) {
269    interstitial_page_->DontProceed();
270  } else if (cmd == CMD_PROCEED) {
271    interstitial_page_->Proceed();
272  } else if (cmd == CMD_FOCUS) {
273    // Start recording the time when the page is first in focus
274    display_start_time_ = base::TimeTicks::Now();
275  } else if (cmd == CMD_MORE) {
276    RecordSSLBlockingPageEventStats(MORE);
277  }
278}
279
280void SSLBlockingPage::OverrideRendererPrefs(
281      content::RendererPreferences* prefs) {
282  Profile* profile = Profile::FromBrowserContext(
283      web_contents_->GetBrowserContext());
284  renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
285}
286
287void SSLBlockingPage::OnProceed() {
288  RecordSSLBlockingPageDetailedStats(true,
289                                     cert_error_,
290                                     overridable_ && !strict_enforcement_,
291                                     internal_,
292                                     display_start_time_,
293                                     num_visits_);
294  // Accepting the certificate resumes the loading of the page.
295  NotifyAllowCertificate();
296}
297
298void SSLBlockingPage::OnDontProceed() {
299  RecordSSLBlockingPageDetailedStats(false,
300                                     cert_error_,
301                                     overridable_ && !strict_enforcement_,
302                                     internal_,
303                                     display_start_time_,
304                                     num_visits_);
305  NotifyDenyCertificate();
306}
307
308void SSLBlockingPage::NotifyDenyCertificate() {
309  // It's possible that callback_ may not exist if the user clicks "Proceed"
310  // followed by pressing the back button before the interstitial is hidden.
311  // In that case the certificate will still be treated as allowed.
312  if (callback_.is_null())
313    return;
314
315  callback_.Run(false);
316  callback_.Reset();
317}
318
319void SSLBlockingPage::NotifyAllowCertificate() {
320  DCHECK(!callback_.is_null());
321
322  callback_.Run(true);
323  callback_.Reset();
324}
325
326// static
327void SSLBlockingPage::SetExtraInfo(
328    DictionaryValue* strings,
329    const std::vector<string16>& extra_info) {
330  DCHECK_LT(extra_info.size(), 5U);  // We allow 5 paragraphs max.
331  const char* keys[5] = {
332      "moreInfo1", "moreInfo2", "moreInfo3", "moreInfo4", "moreInfo5"
333  };
334  int i;
335  for (i = 0; i < static_cast<int>(extra_info.size()); i++) {
336    strings->SetString(keys[i], extra_info[i]);
337  }
338  for (; i < 5; i++) {
339    strings->SetString(keys[i], std::string());
340  }
341}
342
343void SSLBlockingPage::OnGotHistoryCount(HistoryService::Handle handle,
344                                        bool success,
345                                        int num_visits,
346                                        base::Time first_visit) {
347  num_visits_ = num_visits;
348}
349