From 41b717075e6664aa91671ae177d0f39515c38f0f Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Tue, 19 May 2015 13:55:58 -0400 Subject: [PATCH] - Add support for client SSL certificates (issue #1608). - Remember OnCertificateError approvals (issue #1590). --- cef.gyp | 2 + libcef/browser/browser_context_impl.cc | 5 +- libcef/browser/browser_context_impl.h | 2 + libcef/browser/content_browser_client.cc | 12 ++++ libcef/browser/content_browser_client.h | 4 ++ libcef/browser/resource_context.cc | 30 ++++++++ libcef/browser/resource_context.h | 1 + libcef/browser/ssl_host_state_delegate.cc | 86 +++++++++++++++++++++++ libcef/browser/ssl_host_state_delegate.h | 74 +++++++++++++++++++ 9 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 libcef/browser/ssl_host_state_delegate.cc create mode 100644 libcef/browser/ssl_host_state_delegate.h diff --git a/cef.gyp b/cef.gyp index 8582c2c23..9894fe813 100644 --- a/cef.gyp +++ b/cef.gyp @@ -1016,6 +1016,8 @@ 'libcef/browser/speech_recognition_manager_delegate.h', 'libcef/browser/ssl_cert_principal_impl.cc', 'libcef/browser/ssl_cert_principal_impl.h', + 'libcef/browser/ssl_host_state_delegate.cc', + 'libcef/browser/ssl_host_state_delegate.h', 'libcef/browser/ssl_info_impl.cc', 'libcef/browser/ssl_info_impl.h', 'libcef/browser/stream_impl.cc', diff --git a/libcef/browser/browser_context_impl.cc b/libcef/browser/browser_context_impl.cc index ea43188e3..29a8773ec 100644 --- a/libcef/browser/browser_context_impl.cc +++ b/libcef/browser/browser_context_impl.cc @@ -10,6 +10,7 @@ #include "libcef/browser/context.h" #include "libcef/browser/download_manager_delegate.h" #include "libcef/browser/permission_manager.h" +#include "libcef/browser/ssl_host_state_delegate.h" #include "libcef/browser/thread_util.h" #include "base/files/file_util.h" @@ -195,7 +196,9 @@ content::PushMessagingService* content::SSLHostStateDelegate* CefBrowserContextImpl::GetSSLHostStateDelegate() { - return NULL; + if (!ssl_host_state_delegate_.get()) + ssl_host_state_delegate_.reset(new CefSSLHostStateDelegate()); + return ssl_host_state_delegate_.get(); } content::PermissionManager* CefBrowserContextImpl::GetPermissionManager() { diff --git a/libcef/browser/browser_context_impl.h b/libcef/browser/browser_context_impl.h index 0c9c77d0a..992d53ef5 100644 --- a/libcef/browser/browser_context_impl.h +++ b/libcef/browser/browser_context_impl.h @@ -21,6 +21,7 @@ class SpeechRecognitionPreferences; } class CefDownloadManagerDelegate; +class CefSSLHostStateDelegate; // Isolated BrowserContext implementation. Life span is controlled by // CefRequestContextImpl and (for the main context) CefBrowserMainParts. Only @@ -97,6 +98,7 @@ class CefBrowserContextImpl : public CefBrowserContext { scoped_ptr download_manager_delegate_; scoped_refptr url_request_getter_; scoped_ptr permission_manager_; + scoped_ptr ssl_host_state_delegate_; DISALLOW_COPY_AND_ASSIGN(CefBrowserContextImpl); }; diff --git a/libcef/browser/content_browser_client.cc b/libcef/browser/content_browser_client.cc index dd85ce9a2..22fbb0b67 100644 --- a/libcef/browser/content_browser_client.cc +++ b/libcef/browser/content_browser_client.cc @@ -37,6 +37,7 @@ #include "content/public/browser/access_token_store.h" #include "content/public/browser/browser_url_handler.h" #include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/plugin_service_filter.h" #include "content/public/browser/quota_permission_context.h" #include "content/public/browser/render_process_host.h" @@ -45,6 +46,7 @@ #include "content/public/common/content_switches.h" #include "content/public/common/storage_quota_params.h" #include "content/public/common/web_preferences.h" +#include "net/ssl/ssl_cert_request_info.h" #include "third_party/WebKit/public/web/WebWindowFeatures.h" #include "ui/base/ui_base_switches.h" #include "url/gurl.h" @@ -745,6 +747,16 @@ void CefContentBrowserClient::AllowCertificateError( content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL; } +void CefContentBrowserClient::SelectClientCertificate( + content::WebContents* web_contents, + net::SSLCertRequestInfo* cert_request_info, + scoped_ptr delegate) { + if (!cert_request_info->client_certs.empty()) { + // Use the first certificate. + delegate->ContinueWithCertificate(cert_request_info->client_certs[0].get()); + } +} + content::AccessTokenStore* CefContentBrowserClient::CreateAccessTokenStore() { return new CefAccessTokenStore( browser_main_parts_->browser_context()->request_context().get()); diff --git a/libcef/browser/content_browser_client.h b/libcef/browser/content_browser_client.h index ed89d8b1f..50f52ebde 100644 --- a/libcef/browser/content_browser_client.h +++ b/libcef/browser/content_browser_client.h @@ -105,6 +105,10 @@ class CefContentBrowserClient : public content::ContentBrowserClient { bool expired_previous_decision, const base::Callback& callback, content::CertificateRequestResultType* result) override; + void SelectClientCertificate( + content::WebContents* web_contents, + net::SSLCertRequestInfo* cert_request_info, + scoped_ptr delegate) override; content::AccessTokenStore* CreateAccessTokenStore() override; bool CanCreateWindow(const GURL& opener_url, const GURL& opener_top_level_frame_url, diff --git a/libcef/browser/resource_context.cc b/libcef/browser/resource_context.cc index 725046b6a..08016d7d7 100644 --- a/libcef/browser/resource_context.cc +++ b/libcef/browser/resource_context.cc @@ -8,6 +8,18 @@ #include "base/logging.h" #include "content/public/browser/browser_thread.h" +#if defined(USE_NSS_CERTS) +#include "net/ssl/client_cert_store_nss.h" +#endif + +#if defined(OS_WIN) +#include "net/ssl/client_cert_store_win.h" +#endif + +#if defined(OS_MACOSX) +#include "net/ssl/client_cert_store_mac.h" +#endif + CefResourceContext::CefResourceContext() { } @@ -35,6 +47,24 @@ net::URLRequestContext* CefResourceContext::GetRequestContext() { return getter_->GetURLRequestContext(); } +scoped_ptr CefResourceContext::CreateClientCertStore() { +#if defined(USE_NSS_CERTS) + return scoped_ptr(new net::ClientCertStoreNSS( + net::ClientCertStoreNSS::PasswordDelegateFactory())); +#elif defined(OS_WIN) + return scoped_ptr(new net::ClientCertStoreWin()); +#elif defined(OS_MACOSX) + return scoped_ptr(new net::ClientCertStoreMac()); +#elif defined(USE_OPENSSL) + // OpenSSL does not use the ClientCertStore infrastructure. On Android client + // cert matching is done by the OS as part of the call to show the cert + // selection dialog. + return scoped_ptr(); +#else +#error Unknown platform. +#endif +} + void CefResourceContext::set_url_request_context_getter( scoped_refptr getter) { DCHECK(!getter_.get()); diff --git a/libcef/browser/resource_context.h b/libcef/browser/resource_context.h index 34e7154bd..b22a932e0 100644 --- a/libcef/browser/resource_context.h +++ b/libcef/browser/resource_context.h @@ -26,6 +26,7 @@ class CefResourceContext : public content::ResourceContext { // ResourceContext implementation. net::HostResolver* GetHostResolver() override; net::URLRequestContext* GetRequestContext() override; + scoped_ptr CreateClientCertStore() override; void set_url_request_context_getter( scoped_refptr getter); diff --git a/libcef/browser/ssl_host_state_delegate.cc b/libcef/browser/ssl_host_state_delegate.cc new file mode 100644 index 000000000..2ee53d9b8 --- /dev/null +++ b/libcef/browser/ssl_host_state_delegate.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2014 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 "libcef/browser/ssl_host_state_delegate.h" + +#include "net/base/hash_value.h" + +using content::SSLHostStateDelegate; + +namespace internal { + +net::SHA256HashValue getChainFingerprint256(const net::X509Certificate& cert) { + net::SHA256HashValue fingerprint = + net::X509Certificate::CalculateChainFingerprint256( + cert.os_cert_handle(), cert.GetIntermediateCertificates()); + return fingerprint; +} + +CertPolicy::CertPolicy() { +} +CertPolicy::~CertPolicy() { +} + +// For an allowance, we consider a given |cert| to be a match to a saved +// allowed cert if the |error| is an exact match to or subset of the errors +// in the saved CertStatus. +bool CertPolicy::Check(const net::X509Certificate& cert, + net::CertStatus error) const { + net::SHA256HashValue fingerprint = getChainFingerprint256(cert); + std::map::const_iterator allowed_iter = + allowed_.find(fingerprint); + if ((allowed_iter != allowed_.end()) && (allowed_iter->second & error) && + ((allowed_iter->second & error) == error)) { + return true; + } + return false; +} + +void CertPolicy::Allow(const net::X509Certificate& cert, + net::CertStatus error) { + // If this same cert had already been saved with a different error status, + // this will replace it with the new error status. + net::SHA256HashValue fingerprint = getChainFingerprint256(cert); + allowed_[fingerprint] = error; +} + +} // namespace internal + +CefSSLHostStateDelegate::CefSSLHostStateDelegate() { +} + +CefSSLHostStateDelegate::~CefSSLHostStateDelegate() { +} + +void CefSSLHostStateDelegate::HostRanInsecureContent(const std::string& host, + int pid) { + // Intentional no-op. +} + +bool CefSSLHostStateDelegate::DidHostRunInsecureContent(const std::string& host, + int pid) const { + // Intentional no-op. + return false; +} + +void CefSSLHostStateDelegate::AllowCert(const std::string& host, + const net::X509Certificate& cert, + net::CertStatus error) { + cert_policy_for_host_[host].Allow(cert, error); +} + +void CefSSLHostStateDelegate::Clear() { + cert_policy_for_host_.clear(); +} + +SSLHostStateDelegate::CertJudgment CefSSLHostStateDelegate::QueryPolicy( + const std::string& host, + const net::X509Certificate& cert, + net::CertStatus error, + bool* expired_previous_decision) { + return cert_policy_for_host_[host].Check(cert, error) + ? SSLHostStateDelegate::ALLOWED + : SSLHostStateDelegate::DENIED; +} diff --git a/libcef/browser/ssl_host_state_delegate.h b/libcef/browser/ssl_host_state_delegate.h new file mode 100644 index 000000000..97d053150 --- /dev/null +++ b/libcef/browser/ssl_host_state_delegate.h @@ -0,0 +1,74 @@ +// Copyright (c) 2014 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 CEF_LIBCEF_BROWSER_SSL_HOST_STATE_DELEGATE_H_ +#define CEF_LIBCEF_BROWSER_SSL_HOST_STATE_DELEGATE_H_ + +#include +#include + +#include "content/public/browser/ssl_host_state_delegate.h" +#include "net/base/hash_value.h" +#include "net/cert/cert_status_flags.h" +#include "net/cert/x509_certificate.h" + +namespace internal { + +// This class maintains the policy for storing actions on certificate errors. +class CertPolicy { + public: + CertPolicy(); + ~CertPolicy(); + // Returns true if the user has decided to proceed through the ssl error + // before. For a certificate to be allowed, it must not have any + // *additional* errors from when it was allowed. + bool Check(const net::X509Certificate& cert, net::CertStatus error) const; + + // Causes the policy to allow this certificate for a given |error|. And + // remember the user's choice. + void Allow(const net::X509Certificate& cert, net::CertStatus error); + + private: + // The set of fingerprints of allowed certificates. + std::map + allowed_; +}; + +} // namespace internal + +class CefSSLHostStateDelegate : public content::SSLHostStateDelegate { + public: + CefSSLHostStateDelegate(); + ~CefSSLHostStateDelegate() override; + + // Records that |cert| is permitted to be used for |host| in the future, for + // a specified |error| type. + void AllowCert(const std::string& host, + const net::X509Certificate& cert, + net::CertStatus error) override; + + void Clear() override; + + // Queries whether |cert| is allowed or denied for |host| and |error|. + content::SSLHostStateDelegate::CertJudgment QueryPolicy( + const std::string& host, + const net::X509Certificate& cert, + net::CertStatus error, + bool* expired_previous_decision) override; + + // Records that a host has run insecure content. + void HostRanInsecureContent(const std::string& host, int pid) override; + + // Returns whether the specified host ran insecure content. + bool DidHostRunInsecureContent(const std::string& host, + int pid) const override; + + private: + // Certificate policies for each host. + std::map cert_policy_for_host_; + + DISALLOW_COPY_AND_ASSIGN(CefSSLHostStateDelegate); +}; + +#endif // CEF_LIBCEF_BROWSER_SSL_HOST_STATE_DELEGATE_H_