blob: ceb7f83d5884336f11e6144746c4991887c30b67 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// 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_HANDLER_H_
#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SSL_ERROR_HANDLER_H_
#include <string>
#include "base/feature_list.h"
#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "components/captive_portal/content/captive_portal_service.h"
#include "components/captive_portal/core/buildflags.h"
#include "components/security_interstitials/content/common_name_mismatch_handler.h"
#include "components/security_interstitials/content/security_interstitial_page.h"
#include "components/security_interstitials/content/ssl_error_assistant.pb.h"
#include "components/ssl_errors/error_classification.h"
#include "content/public/browser/certificate_request_result_type.h"
#include "content/public/browser/restore_type.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "net/ssl/ssl_info.h"
#include "url/gurl.h"
class SecurityBlockingPageFactory;
class CommonNameMismatchHandler;
struct DynamicInterstitialInfo;
namespace base {
class Clock;
class TimeDelta;
} // namespace base
namespace content {
class WebContents;
}
namespace network_time {
class NetworkTimeTracker;
}
BASE_DECLARE_FEATURE(kMITMSoftwareInterstitial);
// This class is responsible for deciding what type of interstitial to display
// for an SSL validation error and actually displaying it. The display of the
// interstitial might be delayed by a few seconds while trying to determine the
// cause of the error. During this window, the class will:
// - Check for a clock error
// - Check for a known captive portal certificate SPKI
// - Wait for a name-mismatch suggested URL
// - or Wait for a captive portal result to arrive.
//
// Based on the result of these checks, SSLErrorHandler will show a customized
// interstitial, redirect to a different suggested URL, or, if all else fails,
// show the normal SSL interstitial. When --committed--interstitials is enabled,
// it passes a constructed blocking page to the |blocking_page_ready_callback|
// that was given to HandleSSLError(), instead of showing the interstitial
// directly.
//
// This class should only be used on the UI thread because its implementation
// uses captive_portal::CaptivePortalService which can only be accessed on the
// UI thread.
class SSLErrorHandler : public content::WebContentsUserData<SSLErrorHandler>,
public content::WebContentsObserver {
public:
typedef base::RepeatingCallback<void(content::WebContents*)>
TimerStartedCallback;
typedef base::OnceCallback<void(
std::unique_ptr<security_interstitials::SecurityInterstitialPage>)>
BlockingPageReadyCallback;
// Callback that is optionally used to inform the client that a blocking page
// has been shown in the specified WebContents for the specified URL with the
// given error string and network error code.
typedef base::RepeatingCallback<
void(content::WebContents*, const GURL&, const std::string&, int)>
OnBlockingPageShownCallback;
SSLErrorHandler(const SSLErrorHandler&) = delete;
SSLErrorHandler& operator=(const SSLErrorHandler&) = delete;
~SSLErrorHandler() override;
// Events for UMA. Do not rename or remove values, add new values to the end.
// Public for testing.
// If you change this, change the values in CaptivePortalTest.java as well.
enum UMAEvent {
HANDLE_ALL = 0,
SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE = 1,
SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE = 2,
SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE = 3,
SHOW_SSL_INTERSTITIAL_OVERRIDABLE = 4,
WWW_MISMATCH_FOUND = 5, // Deprecated in M59 by WWW_MISMATCH_FOUND_IN_SAN.
WWW_MISMATCH_URL_AVAILABLE = 6,
WWW_MISMATCH_URL_NOT_AVAILABLE = 7,
SHOW_BAD_CLOCK = 8,
CAPTIVE_PORTAL_CERT_FOUND = 9,
WWW_MISMATCH_FOUND_IN_SAN = 10,
SHOW_MITM_SOFTWARE_INTERSTITIAL = 11,
OS_REPORTS_CAPTIVE_PORTAL = 12,
SHOW_BLOCKED_INTERCEPTION_INTERSTITIAL = 13,
SHOW_LEGACY_TLS_INTERSTITIAL = 14, // Deprecated in M98.
SSL_ERROR_HANDLER_EVENT_COUNT
};
// This delegate allows unit tests to provide their own Chrome specific
// actions.
class Delegate {
public:
virtual ~Delegate() = default;
virtual void CheckForCaptivePortal() = 0;
virtual bool DoesOSReportCaptivePortal() = 0;
virtual bool GetSuggestedUrl(const std::vector<std::string>& dns_names,
GURL* suggested_url) const = 0;
virtual void CheckSuggestedUrl(
const GURL& suggested_url,
CommonNameMismatchHandler::CheckUrlCallback callback) = 0;
virtual void NavigateToSuggestedURL(const GURL& suggested_url) = 0;
virtual bool IsErrorOverridable() const = 0;
virtual void ShowCaptivePortalInterstitial(const GURL& landing_url) = 0;
virtual void ShowMITMSoftwareInterstitial(
const std::string& mitm_software_name) = 0;
virtual void ShowSSLInterstitial(const GURL& support_url) = 0;
virtual void ShowBadClockInterstitial(
const base::Time& now,
ssl_errors::ClockState clock_state) = 0;
virtual void ShowBlockedInterceptionInterstitial() = 0;
virtual void ReportNetworkConnectivity(base::OnceClosure callback) = 0;
virtual bool HasBlockedInterception() const = 0;
};
// Entry point for the class. Most parameters are the same as
// SSLBlockingPage constructor.
// Extra parameters:
// |blocking_page_ready_callback| is intended for committed interstitials. If
// |blocking_page_ready_callback| is null, this function will create a
// blocking page and call Show() on it. Otherwise, this function creates an
// interstitial and passes it to |blocking_page_ready_callback|.
// |blocking_page_ready_callback| is guaranteed not to be called
// synchronously.
// |user_can_proceed_past_interstitial| can be given a value of false to
// change the default behavior of giving users the option to proceed past
// SSL error interstitials.
// |blocking_page_factory| will be used to create the interstitial pages
// shown.
static void HandleSSLError(
content::WebContents* web_contents,
int cert_error,
const net::SSLInfo& ssl_info,
const GURL& request_url,
BlockingPageReadyCallback blocking_page_ready_callback,
network_time::NetworkTimeTracker* network_time_tracker,
captive_portal::CaptivePortalService* captive_portal_service,
std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory,
bool user_can_proceed_past_interstitial = true);
// Sets the binary proto for SSL error assistant. The binary proto
// can be downloaded by the component updater, or set by tests.
static void SetErrorAssistantProto(
std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig>
config_proto);
// Invoke this method to have |callback| called whenever an interstitial is
// shown in an SSLErrorHandler instance.
static void SetClientCallbackOnInterstitialsShown(
OnBlockingPageShownCallback callback);
// Testing methods.
static void ResetConfigForTesting();
static void SetInterstitialDelayForTesting(const base::TimeDelta& delay);
// The callback pointer must remain valid for the duration of error handling.
static void SetInterstitialTimerStartedCallbackForTesting(
TimerStartedCallback* callback);
static void SetClockForTesting(base::Clock* testing_clock);
static void SetReportNetworkConnectivityCallbackForTesting(
base::OnceClosure callback);
static void SetEnterpriseManagedForTesting(bool enterprise_managed);
static bool IsEnterpriseManagedFlagSetForTesting();
static std::string GetHistogramNameForTesting();
static int GetErrorAssistantProtoVersionIdForTesting();
static void SetOSReportsCaptivePortalForTesting(
bool os_reports_captive_portal);
bool IsTimerRunningForTesting() const;
protected:
SSLErrorHandler(std::unique_ptr<Delegate> delegate,
content::WebContents* web_contents,
int cert_error,
const net::SSLInfo& ssl_info,
network_time::NetworkTimeTracker* network_time_tracker,
captive_portal::CaptivePortalService* captive_portal_service,
const GURL& request_url);
// Called when an SSL cert error is encountered. Triggers a captive portal
// check and fires a one shot timer to wait for a "captive portal detected"
// result to arrive. Protected for testing.
void StartHandlingError();
private:
friend class content::WebContentsUserData<SSLErrorHandler>;
FRIEND_TEST_ALL_PREFIXES(SSLErrorHandlerTest, CalculateOptionsMask);
FRIEND_TEST_ALL_PREFIXES(SSLErrorHandlerTest,
NonPrimaryMainframeShouldNotAffectSSLErrorHandler);
FRIEND_TEST_ALL_PREFIXES(SSLErrorHandlerNameMismatchTest,
ShouldShowCustomInterstitialOnCaptivePortalResult);
FRIEND_TEST_ALL_PREFIXES(SSLErrorHandlerNameMismatchTest,
ShouldShowSSLInterstitialOnNoCaptivePortalResult);
void ShowCaptivePortalInterstitial(const GURL& landing_url);
void ShowMITMSoftwareInterstitial(const std::string& mitm_software_name);
void ShowSSLInterstitial();
void ShowBadClockInterstitial(const base::Time& now,
ssl_errors::ClockState clock_state);
void ShowDynamicInterstitial(const DynamicInterstitialInfo interstitial);
void ShowBlockedInterceptionInterstitial();
// Gets the result of whether the suggested URL is valid. Displays
// common name mismatch interstitial or ssl interstitial accordingly.
void CommonNameMismatchHandlerCallback(
CommonNameMismatchHandler::SuggestedUrlCheckResult result,
const GURL& suggested_url);
// Callback invoked with the result of a query for captive portal status.
void Observe(const captive_portal::CaptivePortalService::Results& results);
// content::WebContentsObserver:
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override;
// content::WebContentsObserver:
void NavigationStopped() override;
// Deletes the SSLErrorHandler. This method is called when the page
// load stops or when there is a new navigation.
void DeleteSSLErrorHandler();
void HandleCertDateInvalidError();
void HandleCertDateInvalidErrorImpl(base::TimeTicks started_handling_error);
bool IsOnlyCertError(net::CertStatus only_cert_error_expected) const;
std::unique_ptr<Delegate> delegate_;
const int cert_error_;
const net::SSLInfo ssl_info_;
const GURL request_url_;
raw_ptr<network_time::NetworkTimeTracker> network_time_tracker_;
// The below field is unused if captive portal detection is not enabled,
// which causes a compiler error.
#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
raw_ptr<captive_portal::CaptivePortalService, DanglingUntriaged>
captive_portal_service_;
#endif
base::CallbackListSubscription subscription_;
base::OneShotTimer timer_;
std::unique_ptr<CommonNameMismatchHandler> common_name_mismatch_handler_;
base::WeakPtrFactory<SSLErrorHandler> weak_ptr_factory_{this};
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
#endif // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SSL_ERROR_HANDLER_H_