Implement OnSelectClientCertificate (fixes #3789)

This commit is contained in:
Hunter Laux 2024-10-23 12:57:53 -04:00 committed by Marshall Greenblatt
parent 030272fe4c
commit fc20e76d3b
6 changed files with 194 additions and 13 deletions

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=db7cfb76483d6ab73eba74deaefafa7700ad1988$
// $hash=cf3b97164ac99d564ebcd30f927e1331949acbe5$
//
#ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_HANDLER_CAPI_H_
@ -194,16 +194,19 @@ typedef struct _cef_request_handler_t {
///
/// Called on the UI thread when a client certificate is being requested for
/// authentication. Return false (0) to use the default behavior and
/// automatically select the first certificate available. Return true (1) and
/// call cef_select_client_certificate_callback_t::Select either in this
/// function or at a later time to select a certificate. Do not call Select or
/// call it with NULL to continue without using any certificate. |isProxy|
/// indicates whether the host is an HTTPS proxy or the origin server. |host|
/// and |port| contains the hostname and port of the SSL server.
/// |certificates| is the list of certificates to choose from; this list has
/// already been pruned by Chromium so that it only contains certificates from
/// issuers that the server trusts.
/// authentication. Return false (0) to use the default behavior. If the
/// |certificates| list is not NULL the default behavior will be to display a
/// dialog for certificate selection. If the |certificates| list is NULL then
/// the default behavior will be not to show a dialog and it will continue
/// without using any certificate. Return true (1) and call
/// cef_select_client_certificate_callback_t::Select either in this function
/// or at a later time to select a certificate. Do not call Select or call it
/// with NULL to continue without using any certificate. |isProxy| indicates
/// whether the host is an HTTPS proxy or the origin server. |host| and |port|
/// contains the hostname and port of the SSL server. |certificates| is the
/// list of certificates to choose from; this list has already been pruned by
/// Chromium so that it only contains certificates from issuers that the
/// server trusts.
///
int(CEF_CALLBACK* on_select_client_certificate)(
struct _cef_request_handler_t* self,

View File

@ -192,8 +192,11 @@ class CefRequestHandler : public virtual CefBaseRefCounted {
///
/// Called on the UI thread when a client certificate is being requested for
/// authentication. Return false to use the default behavior and automatically
/// select the first certificate available. Return true and call
/// authentication. Return false to use the default behavior. If the
/// |certificates| list is not empty the default behavior will be to display a
/// dialog for certificate selection. If the |certificates| list is empty then
/// the default behavior will be not to show a dialog and it will continue
/// without using any certificate. Return true and call
/// CefSelectClientCertificateCallback::Select either in this method or at a
/// later time to select a certificate. Do not call Select or call it with
/// NULL to continue without using any certificate. |isProxy| indicates

View File

@ -23,6 +23,7 @@
#include "cef/libcef/browser/net_service/resource_request_handler_wrapper.h"
#include "cef/libcef/browser/prefs/browser_prefs.h"
#include "cef/libcef/browser/prefs/renderer_prefs.h"
#include "cef/libcef/browser/x509_certificate_impl.h"
#include "cef/libcef/common/app_manager.h"
#include "cef/libcef/common/cef_switches.h"
#include "cef/libcef/common/command_line_impl.h"
@ -32,6 +33,7 @@
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "components/performance_manager/embedder/performance_manager_registry.h"
#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
@ -39,6 +41,8 @@
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/weak_document_ptr.h"
#include "content/public/common/content_switches.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_private_key.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
@ -48,6 +52,82 @@
namespace {
class CefSelectClientCertificateCallbackImpl
: public CefSelectClientCertificateCallback {
public:
explicit CefSelectClientCertificateCallbackImpl(
std::unique_ptr<content::ClientCertificateDelegate> delegate)
: delegate_(std::move(delegate)) {}
CefSelectClientCertificateCallbackImpl(
const CefSelectClientCertificateCallbackImpl&) = delete;
CefSelectClientCertificateCallbackImpl& operator=(
const CefSelectClientCertificateCallbackImpl&) = delete;
~CefSelectClientCertificateCallbackImpl() override {
// If Select has not been called, call it with NULL to continue without any
// client certificate.
RunNow(std::move(delegate_), nullptr);
}
void Select(CefRefPtr<CefX509Certificate> cert) override {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(&CefSelectClientCertificateCallbackImpl::Select, this,
cert));
} else {
RunNow(std::move(delegate_), cert);
}
}
[[nodiscard]] std::unique_ptr<content::ClientCertificateDelegate>
DisconnectDelegate() {
CEF_REQUIRE_UIT();
return std::move(delegate_);
}
private:
static void RunNow(
std::unique_ptr<content::ClientCertificateDelegate> delegate,
CefRefPtr<CefX509Certificate> cert) {
CEF_REQUIRE_UIT();
if (delegate) {
if (cert) {
CefX509CertificateImpl* certImpl =
static_cast<CefX509CertificateImpl*>(cert.get());
certImpl->AcquirePrivateKey(base::BindOnce(
&CefSelectClientCertificateCallbackImpl::RunWithPrivateKey,
std::move(delegate), cert));
return;
}
delegate->ContinueWithCertificate(nullptr, nullptr);
}
}
static void RunWithPrivateKey(
std::unique_ptr<content::ClientCertificateDelegate> delegate,
CefRefPtr<CefX509Certificate> cert,
scoped_refptr<net::SSLPrivateKey> key) {
CEF_REQUIRE_UIT();
DCHECK(cert);
if (key) {
CefX509CertificateImpl* certImpl =
static_cast<CefX509CertificateImpl*>(cert.get());
delegate->ContinueWithCertificate(certImpl->GetInternalCertObject(), key);
} else {
delegate->ContinueWithCertificate(nullptr, nullptr);
}
}
std::unique_ptr<content::ClientCertificateDelegate> delegate_;
IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefSelectClientCertificateCallbackImpl);
};
void HandleExternalProtocolHelper(
ChromeContentBrowserClientCef* self,
content::WebContents::Getter web_contents_getter,
@ -201,6 +281,64 @@ void ChromeContentBrowserClientCef::AllowCertificateError(
strict_enforcement, std::move(returned_callback));
}
base::OnceClosure ChromeContentBrowserClientCef::SelectClientCertificate(
content::BrowserContext* browser_context,
int process_id,
content::WebContents* web_contents,
net::SSLCertRequestInfo* cert_request_info,
net::ClientCertIdentityList client_certs,
std::unique_ptr<content::ClientCertificateDelegate> delegate) {
CEF_REQUIRE_UIT();
CefRefPtr<CefRequestHandler> handler;
CefRefPtr<CefBrowserHostBase> browser =
CefBrowserHostBase::GetBrowserForContents(web_contents);
if (browser) {
if (auto client = browser->GetClient()) {
handler = client->GetRequestHandler();
}
}
if (!handler) {
return ChromeContentBrowserClient::SelectClientCertificate(
browser_context, process_id, web_contents, cert_request_info,
std::move(client_certs), std::move(delegate));
}
CefRequestHandler::X509CertificateList certs;
for (auto& client_cert : client_certs) {
certs.push_back(new CefX509CertificateImpl(std::move(client_cert)));
}
CefRefPtr<CefSelectClientCertificateCallbackImpl> callbackImpl(
new CefSelectClientCertificateCallbackImpl(std::move(delegate)));
bool handled = handler->OnSelectClientCertificate(
browser.get(), cert_request_info->is_proxy,
cert_request_info->host_and_port.host(),
cert_request_info->host_and_port.port(), certs, callbackImpl.get());
if (!handled) {
delegate = callbackImpl->DisconnectDelegate();
if (delegate) {
client_certs.clear();
for (auto& cert : certs) {
CefX509CertificateImpl* certImpl =
static_cast<CefX509CertificateImpl*>(cert.get());
client_certs.push_back(certImpl->DisconnectIdentity());
}
return ChromeContentBrowserClient::SelectClientCertificate(
browser_context, process_id, web_contents, cert_request_info,
std::move(client_certs), std::move(delegate));
} else {
LOG(ERROR) << "Should return true from OnSelectClientCertificate when "
"executing the callback";
}
}
return base::OnceClosure();
}
bool ChromeContentBrowserClientCef::CanCreateWindow(
content::RenderFrameHost* opener,
const GURL& opener_url,

View File

@ -43,6 +43,13 @@ class ChromeContentBrowserClientCef : public ChromeContentBrowserClient {
bool strict_enforcement,
base::OnceCallback<void(content::CertificateRequestResultType)> callback)
override;
base::OnceClosure SelectClientCertificate(
content::BrowserContext* browser_context,
int process_id,
content::WebContents* web_contents,
net::SSLCertRequestInfo* cert_request_info,
net::ClientCertIdentityList client_certs,
std::unique_ptr<content::ClientCertificateDelegate> delegate) override;
bool CanCreateWindow(content::RenderFrameHost* opener,
const GURL& opener_url,
const GURL& opener_top_level_frame_url,

View File

@ -7,6 +7,7 @@
#include "cef/libcef/browser/x509_cert_principal_impl.h"
#include "cef/libcef/common/time_util.h"
#include "net/cert/x509_util.h"
#include "net/ssl/ssl_private_key.h"
namespace {
@ -27,6 +28,10 @@ CefRefPtr<CefBinaryValue> EncodeCertificate(const CRYPTO_BUFFER* cert_buffer,
} // namespace
CefX509CertificateImpl::CefX509CertificateImpl(
std::unique_ptr<net::ClientCertIdentity> identity)
: identity_(std::move(identity)), cert_(identity_->certificate()) {}
CefX509CertificateImpl::CefX509CertificateImpl(
scoped_refptr<net::X509Certificate> cert)
: cert_(cert) {}
@ -94,6 +99,21 @@ size_t CefX509CertificateImpl::GetIssuerChainSize() {
return 0;
}
void CefX509CertificateImpl::AcquirePrivateKey(
base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)>
private_key_callback) {
if (identity_) {
identity_->AcquirePrivateKey(std::move(private_key_callback));
} else {
std::move(private_key_callback).Run(nullptr);
}
}
std::unique_ptr<net::ClientCertIdentity>
CefX509CertificateImpl::DisconnectIdentity() {
return std::move(identity_);
}
void CefX509CertificateImpl::GetEncodedIssuerChain(
CefX509Certificate::IssuerChainBinaryList& chain,
bool der) {

View File

@ -10,6 +10,7 @@
#include "cef/include/cef_x509_certificate.h"
#include "net/cert/x509_certificate.h"
#include "net/ssl/client_cert_identity.h"
// CefX509Certificate implementation
class CefX509CertificateImpl : public CefX509Certificate {
@ -19,6 +20,9 @@ class CefX509CertificateImpl : public CefX509Certificate {
CefX509CertificateImpl(const CefX509CertificateImpl&) = delete;
CefX509CertificateImpl& operator=(const CefX509CertificateImpl&) = delete;
explicit CefX509CertificateImpl(
std::unique_ptr<net::ClientCertIdentity> identity);
// CefX509Certificate methods.
CefRefPtr<CefX509CertPrincipal> GetSubject() override;
CefRefPtr<CefX509CertPrincipal> GetIssuer() override;
@ -32,10 +36,16 @@ class CefX509CertificateImpl : public CefX509Certificate {
void GetPEMEncodedIssuerChain(IssuerChainBinaryList& chain) override;
scoped_refptr<net::X509Certificate> GetInternalCertObject() { return cert_; }
void AcquirePrivateKey(
base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)>
private_key_callback);
[[nodiscard]] std::unique_ptr<net::ClientCertIdentity> DisconnectIdentity();
private:
void GetEncodedIssuerChain(IssuerChainBinaryList& chain, bool der);
std::unique_ptr<net::ClientCertIdentity> identity_;
scoped_refptr<net::X509Certificate> cert_;
IssuerChainBinaryList pem_encoded_issuer_chain_;
IssuerChainBinaryList der_encoded_issuer_chain_;