File: ssl_error_navigation_throttle.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (185 lines) | stat: -rw-r--r-- 8,070 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/security_interstitials/content/ssl_error_navigation_throttle.h"

#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "build/buildflag.h"
#include "components/guest_view/buildflags/buildflags.h"
#include "components/security_interstitials/content/security_interstitial_page.h"
#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
#include "content/public/browser/navigation_handle.h"
#include "net/cert/cert_status_flags.h"

#if BUILDFLAG(ENABLE_GUEST_VIEW)
#include "components/guest_view/browser/guest_view_base.h"
#endif

namespace {

// Returns true if `handle`'s navigation is happening in a WebContents
// that uses SSL interstitials. Returns false if a plain error page should be
// used instead.
bool WebContentsUsesInterstitials(content::NavigationHandle* handle) {
#if !BUILDFLAG(ENABLE_GUEST_VIEW)
  // Guests are the only remaining use of inner WebContents, so without them
  // `handle`'s WebContents is always the outermost one, and should use
  // interstitials.
  return true;
#else
  guest_view::GuestViewBase* guest =
      guest_view::GuestViewBase::FromNavigationHandle(handle);
  if (!guest) {
    // GuestViews are the only remaining inner WebContents, so show an
    // interstitial if this isn't a guest.
    return true;
  }

  // Some guest view types still show SSL interstitials.
  return guest->RequiresSslInterstitials();
#endif
}

}  // namespace

SSLErrorNavigationThrottle::SSLErrorNavigationThrottle(
    content::NavigationThrottleRegistry& registry,
    SSLErrorNavigationThrottle::HandleSSLErrorCallback
        handle_ssl_error_callback,
    IsInHostedAppCallback is_in_hosted_app_callback,
    ShouldIgnoreInterstitialBecauseNavigationDefaultedToHttpsCallback
        should_ignore_interstitial_because_navigation_defaulted_to_https_callback)
    : content::NavigationThrottle(registry),
      handle_ssl_error_callback_(std::move(handle_ssl_error_callback)),
      is_in_hosted_app_callback_(std::move(is_in_hosted_app_callback)),
      should_ignore_interstitial_because_navigation_defaulted_to_https_callback_(
          std::move(
              should_ignore_interstitial_because_navigation_defaulted_to_https_callback)) {
}

SSLErrorNavigationThrottle::~SSLErrorNavigationThrottle() = default;

content::NavigationThrottle::ThrottleCheckResult
SSLErrorNavigationThrottle::WillFailRequest() {
  content::NavigationHandle* handle = navigation_handle();

  // Check the network error code in case we are here due to a non-ssl related
  // error. SSLInfo also needs to be checked to cover cases where an SSL error
  // does not trigger an interstitial, such as chrome://network-errors.
  if (!net::IsCertificateError(handle->GetNetErrorCode()) ||
      !net::IsCertStatusError(
          handle->GetSSLInfo().value_or(net::SSLInfo()).cert_status)) {
    return content::NavigationThrottle::PROCEED;
  }

  // Do not set special error page HTML for non-primary pages (e.g. regular
  // subframe, prerendering, fenced-frame). Those are handled as normal
  // network errors. Some guest views are an exception if kGuestViewMPArch is
  // enabled, as their main frame won't be a primary main frame.
  if (!(handle->IsInPrimaryMainFrame() || handle->IsGuestViewMainFrame()) ||
      !WebContentsUsesInterstitials(handle)) {
    return content::NavigationThrottle::PROCEED;
  }

  // If the scheme of this navigation was upgraded to HTTPS (because the user
  // didn't type a scheme), don't show an error.
  // TypedNavigationUpgradeThrottle or HttpsOnlyModeNavigationThrottle will
  // handle the error and fall back to HTTP as needed.
  if (std::move(
          should_ignore_interstitial_because_navigation_defaulted_to_https_callback_)
          .Run(handle)) {
    return content::NavigationThrottle::PROCEED;
  }

  const net::SSLInfo info = handle->GetSSLInfo().value_or(net::SSLInfo());
  int cert_status = info.cert_status;
  QueueShowInterstitial(std::move(handle_ssl_error_callback_),
                        handle->GetWebContents(), handle->GetNetErrorCode(),
                        cert_status, info, handle->GetURL());
  return content::NavigationThrottle::ThrottleCheckResult(
      content::NavigationThrottle::DEFER);
}

content::NavigationThrottle::ThrottleCheckResult
SSLErrorNavigationThrottle::WillProcessResponse() {
  content::NavigationHandle* handle = navigation_handle();
  // If there was no certificate error, SSLInfo will be empty.
  const net::SSLInfo info = handle->GetSSLInfo().value_or(net::SSLInfo());
  int cert_status = info.cert_status;
  if (!net::IsCertStatusError(cert_status)) {
    return content::NavigationThrottle::PROCEED;
  }

  // Do not set special error page HTML for non-primary pages (e.g. regular
  // subframe, prerendering, fenced-frame). Those are handled as normal
  // network errors.
  if (!(handle->IsInPrimaryMainFrame() || handle->IsGuestViewMainFrame()) ||
      !WebContentsUsesInterstitials(handle)) {
    return content::NavigationThrottle::PROCEED;
  }

  // Hosted Apps should not be allowed to run if there is a problem with their
  // certificate. So, when a user tries to open such an app, we show an
  // interstitial, even if the user has previously clicked through one. Clicking
  // through the interstitial will continue the navigation in a regular browser
  // window.
  if (std::move(is_in_hosted_app_callback_).Run(handle->GetWebContents())) {
    QueueShowInterstitial(std::move(handle_ssl_error_callback_),
                          handle->GetWebContents(),
                          // The navigation handle's net error code will be
                          // net::OK, because the net stack has allowed the
                          // response to proceed. Synthesize a net error from
                          // the cert status instead.
                          net::MapCertStatusToNetError(cert_status),
                          cert_status, info, handle->GetURL());
    return content::NavigationThrottle::ThrottleCheckResult(
        content::NavigationThrottle::DEFER);
  }

  return content::NavigationThrottle::PROCEED;
}

const char* SSLErrorNavigationThrottle::GetNameForLogging() {
  return "SSLErrorNavigationThrottle";
}

void SSLErrorNavigationThrottle::QueueShowInterstitial(
    HandleSSLErrorCallback handle_ssl_error_callback,
    content::WebContents* web_contents,
    int net_error,
    int cert_status,
    const net::SSLInfo& ssl_info,
    const GURL& request_url) {
  // It is safe to call this without posting because SSLErrorHandler will always
  // call ShowInterstitial asynchronously, giving the throttle time to defer the
  // navigation.
  std::move(handle_ssl_error_callback)
      .Run(web_contents, net_error, ssl_info, request_url,
           base::BindOnce(&SSLErrorNavigationThrottle::ShowInterstitial,
                          weak_ptr_factory_.GetWeakPtr(), net_error));
}

void SSLErrorNavigationThrottle::ShowInterstitial(
    int net_error,
    std::unique_ptr<security_interstitials::SecurityInterstitialPage>
        blocking_page) {
  // Get the error page content before giving up ownership of |blocking_page|.
  std::string error_page_content = blocking_page->GetHTMLContents();

  content::NavigationHandle* handle = navigation_handle();

  // Do not display insterstitials for SSL errors from non-primary pages (e.g.
  // prerendering, fenced-frame). For prerendering specifically, we
  // should already have canceled the prerender from OnSSLCertificateError
  // before the throttle runs.
  DCHECK(handle->IsInPrimaryMainFrame() || handle->IsGuestViewMainFrame());

  security_interstitials::SecurityInterstitialTabHelper::AssociateBlockingPage(
      handle, std::move(blocking_page));

  CancelDeferredNavigation(content::NavigationThrottle::ThrottleCheckResult(
      content::NavigationThrottle::CANCEL, static_cast<net::Error>(net_error),
      error_page_content));
}