Componentize SSLErrorNavigationThrottle for reuse by WebLayer
This CL componentizes ssl_error_navigation_throttle.* in preparation
for using it within the implementation of SSL interstitials for
WebLayer. The only meaningful change is to move the concrete detection
of being within hosted apps to be implementation within
chrome_content_browser_client.cc and passed to
SSLErrorNavigationThrottle as a callback.
This CL leaves out the following, which we will undertake as followup:
- Componentization of the unittest. This requires extra work, notably
the componentization of SSLBlockingPage
(which we will be undertaking).
- Putting the moved files into the security_interstitials into their
own namespace. This would just serve to add noise to this CL.
The (internal-only) design doc for this effort is here:
https://docs.google.com/document/d/1ab_zZ6TF6tMZE54IJ2dz8LSwsIp6XEwky9yzH7WPFqk/edit#
Code authored by [email protected]
Change-Id: I2e410f733cbdd7ed41bdbac0318f9dfe8d9c17a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1880032
Commit-Queue: Evan Stade <[email protected]>
Reviewed-by: Carlos IL <[email protected]>
Cr-Commit-Position: refs/heads/master@{#710171}
diff --git a/components/security_interstitials/content/BUILD.gn b/components/security_interstitials/content/BUILD.gn
index c7a16c9..b7964a91 100644
--- a/components/security_interstitials/content/BUILD.gn
+++ b/components/security_interstitials/content/BUILD.gn
@@ -16,6 +16,9 @@
"security_interstitial_page.h",
"security_interstitial_tab_helper.cc",
"security_interstitial_tab_helper.h",
+ "ssl_cert_reporter.h",
+ "ssl_error_navigation_throttle.cc",
+ "ssl_error_navigation_throttle.h",
"unsafe_resource.cc",
"unsafe_resource.h",
"urls.cc",
diff --git a/components/security_interstitials/content/ssl_cert_reporter.h b/components/security_interstitials/content/ssl_cert_reporter.h
new file mode 100644
index 0000000..506b124
--- /dev/null
+++ b/components/security_interstitials/content/ssl_cert_reporter.h
@@ -0,0 +1,22 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SSL_CERT_REPORTER_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SSL_CERT_REPORTER_H_
+
+#include <string>
+
+// An interface used by interstitial pages to send reports of invalid
+// certificate chains.
+class SSLCertReporter {
+ public:
+ virtual ~SSLCertReporter() {}
+
+ // Sends a serialized certificate report to the report collection
+ // endpoint.
+ virtual void ReportInvalidCertificateChain(
+ const std::string& serialized_report) = 0;
+};
+
+#endif // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SSL_CERT_REPORTER_H_
diff --git a/components/security_interstitials/content/ssl_error_navigation_throttle.cc b/components/security_interstitials/content/ssl_error_navigation_throttle.cc
new file mode 100644
index 0000000..0ca91356
--- /dev/null
+++ b/components/security_interstitials/content/ssl_error_navigation_throttle.cc
@@ -0,0 +1,133 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// 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/bind.h"
+#include "base/feature_list.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/buildflag.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"
+
+SSLErrorNavigationThrottle::SSLErrorNavigationThrottle(
+ content::NavigationHandle* navigation_handle,
+ std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
+ SSLErrorNavigationThrottle::HandleSSLErrorCallback
+ handle_ssl_error_callback,
+ IsInHostedAppCallback is_in_hosted_app_callback)
+ : content::NavigationThrottle(navigation_handle),
+ ssl_cert_reporter_(std::move(ssl_cert_reporter)),
+ handle_ssl_error_callback_(std::move(handle_ssl_error_callback)),
+ is_in_hosted_app_callback_(std::move(is_in_hosted_app_callback)) {}
+
+SSLErrorNavigationThrottle::~SSLErrorNavigationThrottle() {}
+
+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 subframes; those are handled as
+ // normal network errors.
+ if (!handle->IsInMainFrame()) {
+ 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(),
+ std::move(ssl_cert_reporter_));
+ 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 subframes; those are handled as
+ // normal network errors.
+ if (!handle->IsInMainFrame()) {
+ 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(), std::move(ssl_cert_reporter_));
+ 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,
+ std::unique_ptr<SSLCertReporter> ssl_cert_reporter) {
+ // 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,
+ std::move(ssl_cert_reporter),
+ base::Callback<void(content::CertificateRequestResultType)>(),
+ 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();
+ security_interstitials::SecurityInterstitialTabHelper::AssociateBlockingPage(
+ handle->GetWebContents(), handle->GetNavigationId(),
+ std::move(blocking_page));
+
+ CancelDeferredNavigation(content::NavigationThrottle::ThrottleCheckResult(
+ content::NavigationThrottle::CANCEL, static_cast<net::Error>(net_error),
+ error_page_content));
+}
diff --git a/components/security_interstitials/content/ssl_error_navigation_throttle.h b/components/security_interstitials/content/ssl_error_navigation_throttle.h
new file mode 100644
index 0000000..d675458
--- /dev/null
+++ b/components/security_interstitials/content/ssl_error_navigation_throttle.h
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SSL_ERROR_NAVIGATION_THROTTLE_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SSL_ERROR_NAVIGATION_THROTTLE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "components/security_interstitials/content/ssl_cert_reporter.h"
+#include "content/public/browser/certificate_request_result_type.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "net/ssl/ssl_info.h"
+
+class SSLCertReporter;
+
+namespace content {
+class NavigationHandle;
+class WebContents;
+} // namespace content
+
+namespace security_interstitials {
+class SecurityInterstitialPage;
+} // namespace security_interstitials
+
+// SSLErrorNavigationThrottle watches for failed navigations that should be
+// displayed as SSL interstitial pages. More specifically,
+// SSLErrorNavigationThrottle::WillFailRequest() will defer any navigations that
+// failed due to a certificate error. After calculating which interstitial to
+// show, it will cancel the navigation with the interstitial's custom error page
+// HTML.
+class SSLErrorNavigationThrottle : public content::NavigationThrottle {
+ public:
+ typedef base::OnceCallback<void(
+ content::WebContents* web_contents,
+ int cert_error,
+ const net::SSLInfo& ssl_info,
+ const GURL& request_url,
+ std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
+ const base::Callback<void(content::CertificateRequestResultType)>&
+ decision_callback,
+ base::OnceCallback<void(
+ std::unique_ptr<security_interstitials::SecurityInterstitialPage>)>
+ blocking_page_ready_callback)>
+ HandleSSLErrorCallback;
+
+ // Returns whether |web_contents| is in the context of a hosted app, as the
+ // logic of when to display interstitials for SSL errors is specialized for
+ // hosted apps. This is exposed as a callback because although the WebContents
+ // is known at the time of creating SSLErrorNavigationThrottle, it may not
+ // have been inserted into a browser by the time the navigation begins. See
+ // browser_navigator.cc.
+ typedef base::OnceCallback<bool(content::WebContents* web_contents)>
+ IsInHostedAppCallback;
+
+ explicit SSLErrorNavigationThrottle(
+ content::NavigationHandle* handle,
+ std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
+ HandleSSLErrorCallback handle_ssl_error_callback,
+ IsInHostedAppCallback is_in_hosted_app_callback);
+ ~SSLErrorNavigationThrottle() override;
+
+ // content::NavigationThrottle:
+ ThrottleCheckResult WillFailRequest() override;
+ ThrottleCheckResult WillProcessResponse() override;
+ const char* GetNameForLogging() override;
+
+ private:
+ void 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,
+ std::unique_ptr<SSLCertReporter> ssl_cert_reporter);
+ void ShowInterstitial(
+ int net_error,
+ std::unique_ptr<security_interstitials::SecurityInterstitialPage>
+ blocking_page);
+
+ std::unique_ptr<SSLCertReporter> ssl_cert_reporter_;
+ HandleSSLErrorCallback handle_ssl_error_callback_;
+ IsInHostedAppCallback is_in_hosted_app_callback_;
+ base::WeakPtrFactory<SSLErrorNavigationThrottle> weak_ptr_factory_{this};
+};
+
+#endif // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SSL_ERROR_NAVIGATION_THROTTLE_H_