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));
}
|