From f385ea1ae53b0b3fc52d4f84d90bd34571e18681 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Tue, 9 Aug 2022 15:43:00 -0400 Subject: [PATCH] chrome: Add support for CefRequestHandler::OnCertificateError (see issue #3148) --- BUILD.gn | 2 + .../alloy/alloy_content_browser_client.cc | 102 +------------- libcef/browser/certificate_query.cc | 130 ++++++++++++++++++ libcef/browser/certificate_query.h | 42 ++++++ .../chrome_content_browser_client_cef.cc | 23 ++++ .../chrome_content_browser_client_cef.h | 9 ++ tests/cefclient/browser/client_handler.cc | 7 +- 7 files changed, 216 insertions(+), 99 deletions(-) create mode 100644 libcef/browser/certificate_query.cc create mode 100644 libcef/browser/certificate_query.h diff --git a/BUILD.gn b/BUILD.gn index 9a8048150..178879a98 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -497,6 +497,8 @@ source_set("libcef_static") { "libcef/browser/browser_platform_delegate_create.cc", "libcef/browser/browser_util.cc", "libcef/browser/browser_util.h", + "libcef/browser/certificate_query.cc", + "libcef/browser/certificate_query.h", "libcef/browser/chrome/browser_delegate.h", "libcef/browser/chrome/browser_platform_delegate_chrome.cc", "libcef/browser/chrome/browser_platform_delegate_chrome.h", diff --git a/libcef/browser/alloy/alloy_content_browser_client.cc b/libcef/browser/alloy/alloy_content_browser_client.cc index 9993a39f2..f1315a207 100644 --- a/libcef/browser/alloy/alloy_content_browser_client.cc +++ b/libcef/browser/alloy/alloy_content_browser_client.cc @@ -19,6 +19,7 @@ #include "libcef/browser/browser_info_manager.h" #include "libcef/browser/browser_manager.h" #include "libcef/browser/browser_platform_delegate.h" +#include "libcef/browser/certificate_query.h" #include "libcef/browser/context.h" #include "libcef/browser/devtools/devtools_manager_delegate.h" #include "libcef/browser/extensions/extension_system.h" @@ -33,7 +34,6 @@ #include "libcef/browser/prefs/renderer_prefs.h" #include "libcef/browser/printing/print_view_manager.h" #include "libcef/browser/speech_recognition_manager_delegate.h" -#include "libcef/browser/ssl_info_impl.h" #include "libcef/browser/thread_util.h" #include "libcef/browser/x509_certificate_impl.h" #include "libcef/common/alloy/alloy_content_client.h" @@ -224,65 +224,6 @@ class CefQuotaCallbackImpl : public CefCallback { IMPLEMENT_REFCOUNTING(CefQuotaCallbackImpl); }; -class CefAllowCertificateErrorCallbackImpl : public CefCallback { - public: - using CallbackType = - base::OnceCallback; - - explicit CefAllowCertificateErrorCallbackImpl(CallbackType callback) - : callback_(std::move(callback)) {} - - CefAllowCertificateErrorCallbackImpl( - const CefAllowCertificateErrorCallbackImpl&) = delete; - CefAllowCertificateErrorCallbackImpl& operator=( - const CefAllowCertificateErrorCallbackImpl&) = delete; - - ~CefAllowCertificateErrorCallbackImpl() { - if (!callback_.is_null()) { - // The callback is still pending. Cancel it now. - if (CEF_CURRENTLY_ON_UIT()) { - RunNow(std::move(callback_), false); - } else { - CEF_POST_TASK( - CEF_UIT, - base::BindOnce(&CefAllowCertificateErrorCallbackImpl::RunNow, - std::move(callback_), false)); - } - } - } - - void Continue() override { ContinueNow(true); } - - void Cancel() override { ContinueNow(false); } - - [[nodiscard]] CallbackType Disconnect() { return std::move(callback_); } - - private: - void ContinueNow(bool allow) { - if (CEF_CURRENTLY_ON_UIT()) { - if (!callback_.is_null()) { - RunNow(std::move(callback_), allow); - } - } else { - CEF_POST_TASK( - CEF_UIT, - base::BindOnce(&CefAllowCertificateErrorCallbackImpl::ContinueNow, - this, allow)); - } - } - - static void RunNow(CallbackType callback, bool allow) { - CEF_REQUIRE_UIT(); - std::move(callback).Run( - allow ? content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE - : content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); - } - - CallbackType callback_; - - IMPLEMENT_REFCOUNTING(CefAllowCertificateErrorCallbackImpl); -}; - class CefSelectClientCertificateCallbackImpl : public CefSelectClientCertificateCallback { public: @@ -868,42 +809,11 @@ void AlloyContentBrowserClient::AllowCertificateError( bool is_main_frame_request, bool strict_enforcement, base::OnceCallback callback) { - CEF_REQUIRE_UIT(); - - if (!is_main_frame_request) { - // A sub-resource has a certificate error. The user doesn't really - // have a context for making the right decision, so block the request - // hard. - std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); - return; - } - - CefRefPtr browser = - AlloyBrowserHostImpl::GetBrowserForContents(web_contents); - if (!browser.get()) - return; - CefRefPtr client = browser->GetClient(); - if (!client.get()) - return; - CefRefPtr handler = client->GetRequestHandler(); - if (!handler.get()) - return; - - CefRefPtr cef_ssl_info = new CefSSLInfoImpl(ssl_info); - - CefRefPtr callbackImpl( - new CefAllowCertificateErrorCallbackImpl(std::move(callback))); - - bool proceed = handler->OnCertificateError( - browser.get(), static_cast(cert_error), - request_url.spec(), cef_ssl_info, callbackImpl.get()); - if (!proceed) { - // |callback| may be null if the user executed it despite returning false. - callback = callbackImpl->Disconnect(); - if (!callback.is_null()) { - std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); - } - } + auto returned_callback = certificate_query::AllowCertificateError( + web_contents, cert_error, ssl_info, request_url, is_main_frame_request, + strict_enforcement, std::move(callback), /*default_disallow=*/true); + // Callback should not be returned. + DCHECK(returned_callback.is_null()); } base::OnceClosure AlloyContentBrowserClient::SelectClientCertificate( diff --git a/libcef/browser/certificate_query.cc b/libcef/browser/certificate_query.cc new file mode 100644 index 000000000..2826121ec --- /dev/null +++ b/libcef/browser/certificate_query.cc @@ -0,0 +1,130 @@ +// Copyright 2022 The Chromium Embedded Framework 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 "libcef/browser/certificate_query.h" + +#include "include/cef_request_handler.h" +#include "libcef/browser/browser_host_base.h" +#include "libcef/browser/ssl_info_impl.h" +#include "libcef/browser/thread_util.h" + +#include "base/callback.h" +#include "base/callback_helpers.h" +#include "base/logging.h" +#include "content/public/browser/web_contents.h" +#include "net/ssl/ssl_info.h" +#include "url/gurl.h" + +namespace certificate_query { + +namespace { + +class CefAllowCertificateErrorCallbackImpl : public CefCallback { + public: + using CallbackType = CertificateErrorCallback; + + explicit CefAllowCertificateErrorCallbackImpl(CallbackType callback) + : callback_(std::move(callback)) {} + + CefAllowCertificateErrorCallbackImpl( + const CefAllowCertificateErrorCallbackImpl&) = delete; + CefAllowCertificateErrorCallbackImpl& operator=( + const CefAllowCertificateErrorCallbackImpl&) = delete; + + ~CefAllowCertificateErrorCallbackImpl() { + if (!callback_.is_null()) { + // The callback is still pending. Cancel it now. + if (CEF_CURRENTLY_ON_UIT()) { + RunNow(std::move(callback_), false); + } else { + CEF_POST_TASK( + CEF_UIT, + base::BindOnce(&CefAllowCertificateErrorCallbackImpl::RunNow, + std::move(callback_), false)); + } + } + } + + void Continue() override { ContinueNow(true); } + + void Cancel() override { ContinueNow(false); } + + [[nodiscard]] CallbackType Disconnect() { return std::move(callback_); } + + private: + void ContinueNow(bool allow) { + if (CEF_CURRENTLY_ON_UIT()) { + if (!callback_.is_null()) { + RunNow(std::move(callback_), allow); + } + } else { + CEF_POST_TASK( + CEF_UIT, + base::BindOnce(&CefAllowCertificateErrorCallbackImpl::ContinueNow, + this, allow)); + } + } + + static void RunNow(CallbackType callback, bool allow) { + CEF_REQUIRE_UIT(); + std::move(callback).Run( + allow ? content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE + : content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); + } + + CallbackType callback_; + + IMPLEMENT_REFCOUNTING(CefAllowCertificateErrorCallbackImpl); +}; + +} // namespace + +CertificateErrorCallback AllowCertificateError( + content::WebContents* web_contents, + int cert_error, + const net::SSLInfo& ssl_info, + const GURL& request_url, + bool is_main_frame_request, + bool strict_enforcement, + CertificateErrorCallback callback, + bool default_disallow) { + CEF_REQUIRE_UIT(); + + if (!is_main_frame_request) { + // A sub-resource has a certificate error. The user doesn't really + // have a context for making the right decision, so block the request + // hard. + std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); + return base::NullCallback(); + } + + if (auto browser = CefBrowserHostBase::GetBrowserForContents(web_contents)) { + if (auto client = browser->GetClient()) { + if (auto handler = client->GetRequestHandler()) { + CefRefPtr sslInfo(new CefSSLInfoImpl(ssl_info)); + CefRefPtr callbackImpl( + new CefAllowCertificateErrorCallbackImpl(std::move(callback))); + + bool proceed = handler->OnCertificateError( + browser.get(), static_cast(cert_error), + request_url.spec(), sslInfo, callbackImpl.get()); + if (!proceed) { + callback = callbackImpl->Disconnect(); + LOG_IF(ERROR, callback.is_null()) + << "Should return true from OnCertificateError when executing " + "the callback"; + } + } + } + } + + if (!callback.is_null() && default_disallow) { + std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); + return base::NullCallback(); + } + + return callback; +} + +} // namespace certificate_query diff --git a/libcef/browser/certificate_query.h b/libcef/browser/certificate_query.h new file mode 100644 index 000000000..f881480cd --- /dev/null +++ b/libcef/browser/certificate_query.h @@ -0,0 +1,42 @@ +// Copyright 2022 The Chromium Embedded Framework 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 CEF_LIBCEF_BROWSER_CERTIFICATE_QUERY_H_ +#define CEF_LIBCEF_BROWSER_CERTIFICATE_QUERY_H_ +#pragma once + +#include "base/callback_forward.h" +#include "content/public/browser/certificate_request_result_type.h" + +namespace content { +class WebContents; +} + +namespace net { +class SSLInfo; +} + +class GURL; + +namespace certificate_query { + +using CertificateErrorCallback = + base::OnceCallback; + +// Called from ContentBrowserClient::AllowCertificateError. +// |callback| will be returned if the request is unhandled and +// |default_disallow| is false. +[[nodiscard]] CertificateErrorCallback AllowCertificateError( + content::WebContents* web_contents, + int cert_error, + const net::SSLInfo& ssl_info, + const GURL& request_url, + bool is_main_frame_request, + bool strict_enforcement, + CertificateErrorCallback callback, + bool default_disallow); + +} // namespace certificate_query + +#endif // CEF_LIBCEF_BROWSER_CERTIFICATE_QUERY_H_ diff --git a/libcef/browser/chrome/chrome_content_browser_client_cef.cc b/libcef/browser/chrome/chrome_content_browser_client_cef.cc index 598edf2b7..682e45f8e 100644 --- a/libcef/browser/chrome/chrome_content_browser_client_cef.cc +++ b/libcef/browser/chrome/chrome_content_browser_client_cef.cc @@ -10,6 +10,7 @@ #include "libcef/browser/browser_frame.h" #include "libcef/browser/browser_info_manager.h" #include "libcef/browser/browser_manager.h" +#include "libcef/browser/certificate_query.h" #include "libcef/browser/chrome/chrome_browser_host_impl.h" #include "libcef/browser/chrome/chrome_browser_main_extra_parts_cef.h" #include "libcef/browser/context.h" @@ -152,6 +153,28 @@ void ChromeContentBrowserClientCef::RenderProcessWillLaunch( host->AddObserver(CefBrowserInfoManager::GetInstance()); } +void ChromeContentBrowserClientCef::AllowCertificateError( + content::WebContents* web_contents, + int cert_error, + const net::SSLInfo& ssl_info, + const GURL& request_url, + bool is_main_frame_request, + bool strict_enforcement, + base::OnceCallback callback) { + auto returned_callback = certificate_query::AllowCertificateError( + web_contents, cert_error, ssl_info, request_url, is_main_frame_request, + strict_enforcement, std::move(callback), /*default_disallow=*/false); + if (returned_callback.is_null()) { + // The error was handled. + return; + } + + // Proceed with default handling. + ChromeContentBrowserClient::AllowCertificateError( + web_contents, cert_error, ssl_info, request_url, is_main_frame_request, + strict_enforcement, std::move(returned_callback)); +} + bool ChromeContentBrowserClientCef::CanCreateWindow( content::RenderFrameHost* opener, const GURL& opener_url, diff --git a/libcef/browser/chrome/chrome_content_browser_client_cef.h b/libcef/browser/chrome/chrome_content_browser_client_cef.h index 645f1db0c..ef2cdd753 100644 --- a/libcef/browser/chrome/chrome_content_browser_client_cef.h +++ b/libcef/browser/chrome/chrome_content_browser_client_cef.h @@ -31,6 +31,15 @@ class ChromeContentBrowserClientCef : public ChromeContentBrowserClient { void AppendExtraCommandLineSwitches(base::CommandLine* command_line, int child_process_id) override; void RenderProcessWillLaunch(content::RenderProcessHost* host) override; + void AllowCertificateError( + content::WebContents* web_contents, + int cert_error, + const net::SSLInfo& ssl_info, + const GURL& request_url, + bool is_main_frame_request, + bool strict_enforcement, + base::OnceCallback callback) + override; bool CanCreateWindow(content::RenderFrameHost* opener, const GURL& opener_url, const GURL& opener_top_level_frame_url, diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index 3e8b319d8..fe4d0ecdc 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -977,9 +977,10 @@ bool ClientHandler::OnCertificateError(CefRefPtr browser, CefRefPtr callback) { CEF_REQUIRE_UI_THREAD(); - if (cert_error == ERR_CERT_AUTHORITY_INVALID && - request_url.ToString().find("https://www.magpcss.org/") == 0U) { - // Allow the CEF Forum to load. It has a self-signed certificate. + if (cert_error == ERR_CERT_COMMON_NAME_INVALID && + request_url.ToString().find("https://www.magpcss.com/") == 0U) { + // Allow magpcss.com to load despite having a certificate common name of + // magpcss.org. callback->Continue(); return true; }