[Curl] Create classes dedicated to handle SSL related tasks
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Sep 2017 15:41:15 +0000 (15:41 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Sep 2017 15:41:15 +0000 (15:41 +0000)
and separate verifier and certificate management.
https://bugs.webkit.org/show_bug.cgi?id=176910

Patch by Basuke Suzuki <Basuke.Suzuki@sony.com> on 2017-09-18
Reviewed by Alex Christensen.

* platform/Curl.cmake:
* platform/network/curl/CurlContext.cpp:
(WebCore::CurlContext::CurlContext):
(WebCore::CurlHandle::setCACertPath):
(WebCore::certificatePath): Deleted.
(WebCore::CurlHandle::enableCAInfoIfExists): Deleted.
(WebCore::CurlHandle::setSslErrors): Deleted.
(WebCore::CurlHandle::getSslErrors): Deleted.
* platform/network/curl/CurlContext.h:
(WebCore::CurlContext::sslHandle):
(WebCore::CurlContext::getCertificatePath const): Deleted.
(WebCore::CurlContext::shouldIgnoreSSLErrors const): Deleted.
* platform/network/curl/CurlDownload.cpp:
(WebCore::CurlDownload::setupRequest):
* platform/network/curl/CurlSSLHandle.cpp: Added.
(WebCore::CurlSSLHandle::CurlSSLHandle):
(WebCore::CurlSSLHandle::getCACertPathEnv):
(WebCore::CurlSSLHandle::setHostAllowsAnyHTTPSCertificate):
(WebCore::CurlSSLHandle::isAllowedHTTPSCertificateHost):
(WebCore::CurlSSLHandle::canIgnoredHTTPSCertificate):
(WebCore::CurlSSLHandle::setClientCertificateInfo):
(WebCore::CurlSSLHandle::getSSLClientCertificate):
* platform/network/curl/CurlSSLHandle.h: Renamed from Source/WebCore/platform/network/curl/SSLHandle.h.
(WebCore::CurlSSLHandle::shouldIgnoreSSLErrors const):
(WebCore::CurlSSLHandle::getCACertPath const):
* platform/network/curl/CurlSSLVerifier.cpp: Renamed from Source/WebCore/platform/network/curl/SSLHandle.cpp.
(WebCore::CurlSSLVerifier::setSslCtx):
(WebCore::CurlSSLVerifier::certVerifyCallback):
(WebCore::CurlSSLVerifier::getPemDataFromCtx):
(WebCore::CurlSSLVerifier::convertToSSLCertificateFlags):
* platform/network/curl/CurlSSLVerifier.h: Added.
(WebCore::CurlSSLVerifier::setCurlHandle):
(WebCore::CurlSSLVerifier::setHostName):
(WebCore::CurlSSLVerifier::sslErrors):
* platform/network/curl/ResourceHandleCurl.cpp:
(WebCore::ResourceHandle::setHostAllowsAnyHTTPSCertificate):
(WebCore::ResourceHandle::setClientCertificateInfo):
* platform/network/curl/ResourceHandleCurlDelegate.cpp:
(WebCore::ResourceHandleCurlDelegate::ResourceHandleCurlDelegate):
(WebCore::ResourceHandleCurlDelegate::setupRequest):
(WebCore::ResourceHandleCurlDelegate::notifyFail):
(WebCore::ResourceHandleCurlDelegate::willSetupSslCtx):
(WebCore::ResourceHandleCurlDelegate::willSetupSslCtxCallback):
* platform/network/curl/ResourceHandleCurlDelegate.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@222147 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
Source/WebCore/ChangeLog
Source/WebCore/platform/Curl.cmake
Source/WebCore/platform/network/curl/CurlContext.cpp
Source/WebCore/platform/network/curl/CurlContext.h
Source/WebCore/platform/network/curl/CurlDownload.cpp
Source/WebCore/platform/network/curl/CurlSSLHandle.cpp [new file with mode: 0644]
Source/WebCore/platform/network/curl/CurlSSLHandle.h [moved from Source/WebCore/platform/network/curl/SSLHandle.h with 52% similarity]
Source/WebCore/platform/network/curl/CurlSSLVerifier.cpp [moved from Source/WebCore/platform/network/curl/SSLHandle.cpp with 72% similarity]
Source/WebCore/platform/network/curl/CurlSSLVerifier.h [new file with mode: 0644]
Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp
Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp
Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.h

index ecafca4..47b80d0 100644 (file)
@@ -1,3 +1,56 @@
+2017-09-18  Basuke Suzuki  <Basuke.Suzuki@sony.com>
+
+        [Curl] Create classes dedicated to handle SSL related tasks
+        and separate verifier and certificate management.
+        https://bugs.webkit.org/show_bug.cgi?id=176910
+
+        Reviewed by Alex Christensen.
+
+        * platform/Curl.cmake:
+        * platform/network/curl/CurlContext.cpp:
+        (WebCore::CurlContext::CurlContext):
+        (WebCore::CurlHandle::setCACertPath):
+        (WebCore::certificatePath): Deleted.
+        (WebCore::CurlHandle::enableCAInfoIfExists): Deleted.
+        (WebCore::CurlHandle::setSslErrors): Deleted.
+        (WebCore::CurlHandle::getSslErrors): Deleted.
+        * platform/network/curl/CurlContext.h:
+        (WebCore::CurlContext::sslHandle):
+        (WebCore::CurlContext::getCertificatePath const): Deleted.
+        (WebCore::CurlContext::shouldIgnoreSSLErrors const): Deleted.
+        * platform/network/curl/CurlDownload.cpp:
+        (WebCore::CurlDownload::setupRequest):
+        * platform/network/curl/CurlSSLHandle.cpp: Added.
+        (WebCore::CurlSSLHandle::CurlSSLHandle):
+        (WebCore::CurlSSLHandle::getCACertPathEnv):
+        (WebCore::CurlSSLHandle::setHostAllowsAnyHTTPSCertificate):
+        (WebCore::CurlSSLHandle::isAllowedHTTPSCertificateHost):
+        (WebCore::CurlSSLHandle::canIgnoredHTTPSCertificate):
+        (WebCore::CurlSSLHandle::setClientCertificateInfo):
+        (WebCore::CurlSSLHandle::getSSLClientCertificate):
+        * platform/network/curl/CurlSSLHandle.h: Renamed from Source/WebCore/platform/network/curl/SSLHandle.h.
+        (WebCore::CurlSSLHandle::shouldIgnoreSSLErrors const):
+        (WebCore::CurlSSLHandle::getCACertPath const):
+        * platform/network/curl/CurlSSLVerifier.cpp: Renamed from Source/WebCore/platform/network/curl/SSLHandle.cpp.
+        (WebCore::CurlSSLVerifier::setSslCtx):
+        (WebCore::CurlSSLVerifier::certVerifyCallback):
+        (WebCore::CurlSSLVerifier::getPemDataFromCtx):
+        (WebCore::CurlSSLVerifier::convertToSSLCertificateFlags):
+        * platform/network/curl/CurlSSLVerifier.h: Added.
+        (WebCore::CurlSSLVerifier::setCurlHandle):
+        (WebCore::CurlSSLVerifier::setHostName):
+        (WebCore::CurlSSLVerifier::sslErrors):
+        * platform/network/curl/ResourceHandleCurl.cpp:
+        (WebCore::ResourceHandle::setHostAllowsAnyHTTPSCertificate):
+        (WebCore::ResourceHandle::setClientCertificateInfo):
+        * platform/network/curl/ResourceHandleCurlDelegate.cpp:
+        (WebCore::ResourceHandleCurlDelegate::ResourceHandleCurlDelegate):
+        (WebCore::ResourceHandleCurlDelegate::setupRequest):
+        (WebCore::ResourceHandleCurlDelegate::notifyFail):
+        (WebCore::ResourceHandleCurlDelegate::willSetupSslCtx):
+        (WebCore::ResourceHandleCurlDelegate::willSetupSslCtxCallback):
+        * platform/network/curl/ResourceHandleCurlDelegate.h:
+
 2017-09-17  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         REGRESSION(r221974): [Harfbuzz] Test fast/text/international/hebrew-selection.html is failing since r221974
index bc12239..f2f6308 100644 (file)
@@ -10,6 +10,8 @@ list(APPEND WebCore_SOURCES
     platform/network/curl/CurlContext.cpp
     platform/network/curl/CurlDownload.cpp
     platform/network/curl/CurlJobManager.cpp
+    platform/network/curl/CurlSSLHandle.cpp
+    platform/network/curl/CurlSSLVerifier.cpp
     platform/network/curl/DNSCurl.cpp
     platform/network/curl/FormDataStreamCurl.cpp
     platform/network/curl/MultipartHandle.cpp
@@ -17,7 +19,6 @@ list(APPEND WebCore_SOURCES
     platform/network/curl/ResourceHandleCurl.cpp
     platform/network/curl/ResourceHandleCurlDelegate.cpp
     platform/network/curl/ResourceResponseCurl.cpp
-    platform/network/curl/SSLHandle.cpp
     platform/network/curl/SocketStreamHandleImplCurl.cpp
     platform/network/curl/SynchronousLoaderClientCurl.cpp
 )
index 8e5962d..0f5d48d 100644 (file)
 #include <shlwapi.h>
 #endif
 
-#if USE(CF)
-#include <wtf/RetainPtr.h>
-#endif
-
 using namespace WebCore;
 
 namespace WebCore {
 
-static CString certificatePath()
-{
-    char* envPath = getenv("CURL_CA_BUNDLE_PATH");
-    if (envPath)
-        return envPath;
-
-#if USE(CF)
-    CFBundleRef webKitBundleRef = webKitBundle();
-    if (webKitBundleRef) {
-        RetainPtr<CFURLRef> certURLRef = adoptCF(CFBundleCopyResourceURL(webKitBundleRef, CFSTR("cacert"), CFSTR("pem"), CFSTR("certificates")));
-        if (certURLRef) {
-            char path[MAX_PATH];
-            CFURLGetFileSystemRepresentation(certURLRef.get(), false, reinterpret_cast<UInt8*>(path), MAX_PATH);
-            return path;
-        }
-    }
-#endif
-
-    return CString();
-}
-
 static CString cookieJarPath()
 {
     char* cookieJarPath = getenv("CURL_COOKIE_JAR_PATH");
@@ -106,13 +81,10 @@ const char* const CurlContext::errorDomain = "CurlErrorDomain";
 
 CurlContext::CurlContext()
 : m_cookieJarFileName { cookieJarPath() }
-, m_certificatePath { certificatePath() }
 , m_cookieJar { std::make_unique<CookieJarCurlFileSystem>() }
 {
     initCookieSession();
 
-    m_ignoreSSLErrors = getenv("WEBKIT_IGNORE_SSL_ERRORS");
-
 #ifndef NDEBUG
     m_verbose = getenv("DEBUG_CURL");
 
@@ -469,11 +441,10 @@ void CurlHandle::setHttpAuthUserPass(const String& user, const String& password)
     curl_easy_setopt(m_handle, CURLOPT_USERPWD, userpass.utf8().data());
 }
 
-void CurlHandle::enableCAInfoIfExists()
+void CurlHandle::setCACertPath(const char* path)
 {
-    const char* certPath = CurlContext::singleton().getCertificatePath();
-    if (certPath)
-        curl_easy_setopt(m_handle, CURLOPT_CAINFO, certPath);
+    if (path)
+        curl_easy_setopt(m_handle, CURLOPT_CAINFO, path);
 }
 
 void CurlHandle::setSslVerifyPeer(VerifyPeer verifyPeer)
@@ -501,16 +472,6 @@ void CurlHandle::setSslKeyPassword(const char* password)
     curl_easy_setopt(m_handle, CURLOPT_KEYPASSWD, password);
 }
 
-void CurlHandle::setSslErrors(unsigned sslErrors)
-{
-    m_sslErrors = sslErrors;
-}
-
-unsigned CurlHandle::getSslErrors()
-{
-    return m_sslErrors;
-}
-
 void CurlHandle::enableCookieJarIfExists()
 {
     const char* cookieJar = CurlContext::singleton().getCookieJarFileName();
index 2b26ddb..14e98a0 100644 (file)
@@ -27,6 +27,7 @@
 #pragma once
 
 #include "CookieJarCurl.h"
+#include "CurlSSLHandle.h"
 #include "URL.h"
 
 #include <wtf/Lock.h>
@@ -120,15 +121,14 @@ public:
     void setCookieJarFileName(const char* cookieJarFileName) { m_cookieJarFileName = CString(cookieJarFileName); }
     CookieJarCurl& cookieJar() { return *m_cookieJar; }
 
-    // Certificate
-    const char* getCertificatePath() const { return m_certificatePath.data(); }
-    bool shouldIgnoreSSLErrors() const { return m_ignoreSSLErrors; }
-
     // Proxy
     const ProxyInfo& proxyInfo() const { return m_proxy; }
     void setProxyInfo(const ProxyInfo& info) { m_proxy = info;  }
     void setProxyInfo(const String& host = emptyString(), unsigned long port = 0, CurlProxyType = CurlProxyType::HTTP, const String& username = emptyString(), const String& password = emptyString());
 
+    // SSL
+    CurlSSLHandle& sslHandle() { return m_sslHandle; }
+
 #ifndef NDEBUG
     FILE* getLogFile() const { return m_logFile; }
     bool isVerbose() const { return m_verbose; }
@@ -137,10 +137,9 @@ public:
 private:
     ProxyInfo m_proxy;
     CString m_cookieJarFileName;
-    CString m_certificatePath;
     CurlShareHandle m_shareHandle;
     std::unique_ptr<CookieJarCurl> m_cookieJar;
-    bool m_ignoreSSLErrors { false };
+    CurlSSLHandle m_sslHandle;
 
     CurlContext();
     void initCookieSession();
@@ -263,14 +262,12 @@ public:
     void enableHttpAuthentication(long);
     void setHttpAuthUserPass(const String&, const String&);
 
-    void enableCAInfoIfExists();
+    void setCACertPath(const char*);
     void setSslVerifyPeer(VerifyPeer);
     void setSslVerifyHost(VerifyHost);
     void setSslCert(const char*);
     void setSslCertType(const char*);
     void setSslKeyPassword(const char*);
-    void setSslErrors(unsigned);
-    unsigned getSslErrors();
 
     void enableCookieJarIfExists();
     void setCookieList(const char*);
@@ -309,7 +306,6 @@ private:
     CURL* m_handle { nullptr };
     char m_errorBuffer[CURL_ERROR_SIZE] { };
     CURLcode m_errorCode;
-    unsigned m_sslErrors { 0 };
 
     char* m_url { nullptr };
     void* m_privateData { nullptr };
index 5f5e3e4..bbf4da1 100644 (file)
@@ -31,6 +31,7 @@
 #if USE(CURL)
 
 #include "CurlContext.h"
+#include "CurlSSLHandle.h"
 #include "HTTPHeaderNames.h"
 #include "HTTPParsers.h"
 #include "ResourceRequest.h"
@@ -119,7 +120,8 @@ void CurlDownload::setupRequest()
     m_curlHandle.setWriteCallbackFunction(writeCallback, this);
     m_curlHandle.enableFollowLocation();
     m_curlHandle.enableHttpAuthentication(CURLAUTH_ANY);
-    m_curlHandle.enableCAInfoIfExists();
+    m_curlHandle.setCACertPath(CurlContext::singleton().sslHandle().getCACertPath());
+
 }
 
 void CurlDownload::notifyFinish()
diff --git a/Source/WebCore/platform/network/curl/CurlSSLHandle.cpp b/Source/WebCore/platform/network/curl/CurlSSLHandle.cpp
new file mode 100644 (file)
index 0000000..3c7fd20
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 University of Szeged
+ * Copyright (C) 2017 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CurlSSLHandle.h"
+
+#if USE(CURL)
+
+#if USE(CF)
+#if OS(WINDOWS)
+#include "WebCoreBundleWin.h"
+#endif
+
+#include <wtf/RetainPtr.h>
+#endif
+
+namespace WebCore {
+
+CurlSSLHandle::CurlSSLHandle()
+    : m_caCertPath(getCACertPathEnv())
+{
+    char* ignoreSSLErrors = getenv("WEBKIT_IGNORE_SSL_ERRORS");
+    if (ignoreSSLErrors)
+        m_ignoreSSLErrors = true;
+}
+
+CString CurlSSLHandle::getCACertPathEnv()
+{
+    char* envPath = getenv("CURL_CA_BUNDLE_PATH");
+    if (envPath)
+        return envPath;
+
+#if USE(CF)
+    CFBundleRef webKitBundleRef = webKitBundle();
+    if (webKitBundleRef) {
+        RetainPtr<CFURLRef> certURLRef = adoptCF(CFBundleCopyResourceURL(webKitBundleRef, CFSTR("cacert"), CFSTR("pem"), CFSTR("certificates")));
+        if (certURLRef) {
+            char path[MAX_PATH];
+            CFURLGetFileSystemRepresentation(certURLRef.get(), false, reinterpret_cast<UInt8*>(path), MAX_PATH);
+            return path;
+        }
+    }
+#endif
+
+    return CString();
+}
+
+void CurlSSLHandle::setHostAllowsAnyHTTPSCertificate(const String& hostName)
+{
+    LockHolder mutex(m_mutex);
+
+    ListHashSet<String> certificates;
+    m_allowedHosts.set(hostName, certificates);
+}
+
+bool CurlSSLHandle::isAllowedHTTPSCertificateHost(const String& hostName)
+{
+    LockHolder mutex(m_mutex);
+
+    auto it = m_allowedHosts.find(hostName);
+    return (it != m_allowedHosts.end());
+}
+
+bool CurlSSLHandle::canIgnoredHTTPSCertificate(const String& hostName, const ListHashSet<String>& certificates)
+{
+    LockHolder mutex(m_mutex);
+
+    auto found = m_allowedHosts.find(hostName);
+    if (found == m_allowedHosts.end())
+        return false;
+
+    auto& value = found->value;
+    if (value.isEmpty()) {
+        value = certificates;
+        return true;
+    }
+
+    return std::equal(certificates.begin(), certificates.end(), value.begin());
+}
+
+void CurlSSLHandle::setClientCertificateInfo(const String& hostName, const String& certificate, const String& key)
+{
+    LockHolder mutex(m_mutex);
+
+    ClientCertificate clientInfo(certificate, key);
+    m_allowedClientHosts.set(hostName, clientInfo);
+}
+
+std::optional<CurlSSLHandle::ClientCertificate> CurlSSLHandle::getSSLClientCertificate(const String& hostName)
+{
+    LockHolder mutex(m_mutex);
+
+    auto it = m_allowedClientHosts.find(hostName);
+    if (it == m_allowedClientHosts.end())
+        return std::nullopt;
+
+    return it->value;
+}
+
+}
+
+#endif
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2013 University of Szeged
+ * Copyright (C) 2017 Sony Interactive Entertainment Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef SSLHandle_h
-#define SSLHandle_h
+#pragma once
 
-#include "ResourceHandle.h"
+#if OS(WINDOWS)
+#include <winsock2.h>
+#endif
 
-#include <wtf/text/WTFString.h>
+#include <openssl/ssl.h>
+#include <wtf/HashMap.h>
+#include <wtf/ListHashSet.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/text/StringHash.h>
 
 namespace WebCore {
 
-class CurlHandle;
+class CurlSSLHandle {
+    WTF_MAKE_NONCOPYABLE(CurlSSLHandle);
+    friend NeverDestroyed<CurlSSLHandle>;
 
-typedef enum {
-    SSL_CERTIFICATE_UNKNOWN_CA = (1 << 0), // The signing certificate authority is not known.
-    SSL_CERTIFICATE_BAD_IDENTITY = (1 << 1), // The certificate does not match the expected identity of the site that it was retrieved from.
-    SSL_CERTIFICATE_NOT_ACTIVATED = (1 << 2), // The certificate's activation time is still in the future
-    SSL_CERTIFICATE_EXPIRED = (1 << 3), // The certificate has expired
-    SSL_CERTIFICATE_REVOKED = (1 << 4), // The certificate has been revoked
-    SSL_CERTIFICATE_INSECURE = (1 << 5), // The certificate's algorithm is considered insecure.
-    SSL_CERTIFICATE_GENERIC_ERROR = (1 << 6) // Some other error occurred validating the certificate
-} SSLCertificateFlags;
+public:
+    CurlSSLHandle();
 
-typedef std::pair<String, String> ClientCertificate;
+    using ClientCertificate = std::pair<String, String>;
 
-void addAllowedClientCertificate(const String&, const String&, const String&);
-void allowsAnyHTTPSCertificateHosts(const String&);
-bool sslIgnoreHTTPSCertificate(const String&, const String&);
-std::optional<ClientCertificate> getSSLClientCertificate(const String&);
-void setSSLVerifyOptions(CurlHandle&);
+    bool shouldIgnoreSSLErrors() const { return m_ignoreSSLErrors; }
+    const char* getCACertPath() const { return m_caCertPath.data(); }
 
-}
+    void setHostAllowsAnyHTTPSCertificate(const String&);
+    bool isAllowedHTTPSCertificateHost(const String&);
+    bool canIgnoredHTTPSCertificate(const String&, const ListHashSet<String>&);
 
-#endif
+    void setClientCertificateInfo(const String&, const String&, const String&);
+    std::optional<ClientCertificate> getSSLClientCertificate(const String&);
+
+private:
+    CString getCACertPathEnv();
+
+    bool m_ignoreSSLErrors { false };
+    CString m_caCertPath;
+
+    Lock m_mutex;
+    HashMap<String, ListHashSet<String>, ASCIICaseInsensitiveHash> m_allowedHosts;
+    HashMap<String, ClientCertificate, ASCIICaseInsensitiveHash> m_allowedClientHosts;
+};
+
+
+}
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2013 University of Szeged
+ * Copyright (C) 2017 Sony Interactive Entertainment Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  */
 
 #include "config.h"
+#include "CurlSSLVerifier.h"
 
 #if USE(CURL)
-
-#include "SSLHandle.h"
-
-#include "ResourceHandleInternal.h"
-
-#include <openssl/pem.h>
-#include <openssl/ssl.h>
-#include <openssl/x509_vfy.h>
-#include <wtf/ListHashSet.h>
-#include <wtf/NeverDestroyed.h>
-#include <wtf/text/CString.h>
+#include "CurlContext.h"
+#include "CurlSSLHandle.h"
 
 namespace WebCore {
 
-static HashMap<String, ListHashSet<String>, ASCIICaseInsensitiveHash>& allowedHosts()
+void CurlSSLVerifier::setSslCtx(void* sslCtx)
 {
-    static NeverDestroyed<HashMap<String, ListHashSet<String>, ASCIICaseInsensitiveHash>> map;
-    return map;
-}
+    if (!sslCtx)
+        return;
 
-static HashMap<String, ClientCertificate, ASCIICaseInsensitiveHash>& allowedClientHosts()
-{
-    static NeverDestroyed<HashMap<String, ClientCertificate, ASCIICaseInsensitiveHash>> map;
-    return map;
+    SSL_CTX_set_app_data(static_cast<SSL_CTX*>(sslCtx), this);
+    SSL_CTX_set_verify(static_cast<SSL_CTX*>(sslCtx), SSL_VERIFY_PEER, certVerifyCallback);
 }
 
-void allowsAnyHTTPSCertificateHosts(const String& host)
+int CurlSSLVerifier::certVerifyCallback(int ok, X509_STORE_CTX* storeCtx)
 {
+    // whether the verification of the certificate in question was passed (preverify_ok=1) or not (preverify_ok=0)
+    int certErrCd = X509_STORE_CTX_get_error(storeCtx);
+    if (!certErrCd)
+        return 1;
+
+    SSL* ssl = static_cast<SSL*>(X509_STORE_CTX_get_ex_data(storeCtx, SSL_get_ex_data_X509_STORE_CTX_idx()));
+    SSL_CTX* sslCtx = SSL_get_SSL_CTX(ssl);
+    CurlSSLVerifier* verifier = static_cast<CurlSSLVerifier*>(SSL_CTX_get_app_data(sslCtx));
+    if (verifier)
+        return 0;
+
+    verifier->m_sslErrors = static_cast<int>(verifier->convertToSSLCertificateFlags(certErrCd));
+
+#if OS(WINDOWS)
+    ok = CurlContext::singleton().sslHandle().isAllowedHTTPSCertificateHost(verifier->m_hostName);
+#else
     ListHashSet<String> certificates;
-    allowedHosts().set(host, certificates);
-}
+    if (!getPemDataFromCtx(storeCtx, certificates))
+        return 0;
 
-void addAllowedClientCertificate(const String& host, const String& certificate, const String& key)
-{
-    ClientCertificate clientInfo { certificate, key };
-    allowedClientHosts().set(host, clientInfo);
-}
+    ok = CurlContext::singleton().sslHandle().canIgnoredHTTPSCertificate(verifier->m_hostName, certificates);
+#endif
 
-std::optional<ClientCertificate> getSSLClientCertificate(const String& host)
-{
-    auto it = allowedClientHosts().find(host);
-    if (it == allowedClientHosts().end())
-        return std::nullopt;
+    if (ok) {
+        // if the host and the certificate are stored for the current handle that means is enabled,
+        // so don't need to curl verifies the authenticity of the peer's certificate
+        if (verifier->m_curlHandle)
+            verifier->m_curlHandle->setSslVerifyPeer(CurlHandle::VerifyPeerDisable);
+    }
 
-    return it->value;
+    return ok;
 }
 
-bool sslIgnoreHTTPSCertificate(const String& host, const ListHashSet<String>& certificates)
+#if !OS(WINDOWS)
+
+bool CurlSSLVerifier::getPemDataFromCtx(X509_STORE_CTX* ctx, ListHashSet<String>& certificates)
 {
-    auto it = allowedHosts().find(host);
-    if (it != allowedHosts().end()) {
-        if ((it->value).isEmpty()) {
-            it->value = certificates;
-            return true;
+    bool ok = true;
+    STACK_OF(X509)* certs = X509_STORE_CTX_get1_chain(ctx);
+    for (int i = 0; i < sk_X509_num(certs); i++) {
+        X509* uCert = sk_X509_value(certs, i);
+        BIO* bio = BIO_new(BIO_s_mem());
+        int res = PEM_write_bio_X509(bio, uCert);
+        if (!res) {
+            ok = false;
+            BIO_free(bio);
+            break;
         }
-        if (certificates.size() != it->value.size())
-            return false;
-        auto certsIter = certificates.begin();
-        auto valueIter = (it->value).begin();
-        for (; valueIter != (it->value).end(); ++valueIter, ++certsIter) {
-            if (*certsIter != *valueIter)
-                return false;
+
+        unsigned char* certificateData;
+        long length = BIO_get_mem_data(bio, &certificateData);
+        if (length < 0) {
+            ok = false;
+            BIO_free(bio);
+            break;
         }
-        return true;
+
+        certificateData[length] = '\0';
+        String certificate = certificateData;
+        certificates.add(certificate);
+        BIO_free(bio);
     }
-    return false;
+
+    sk_X509_pop_free(certs, X509_free);
+    return ok;
 }
 
-unsigned sslCertificateFlag(const unsigned& sslError)
+#endif
+
+CurlSSLVerifier::SSLCertificateFlags CurlSSLVerifier::convertToSSLCertificateFlags(const unsigned& sslError)
 {
     switch (sslError) {
     case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT : // the issuer certificate could not be found: this occurs if the issuer certificate of an untrusted certificate cannot be found.
     case X509_V_ERR_UNABLE_TO_GET_CRL : // the CRL of a certificate could not be found.
     case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY : // the issuer certificate of a locally looked up certificate could not be found. This normally means the list of trusted certificates is not complete.
-        return SSL_CERTIFICATE_UNKNOWN_CA;
+        return SSLCertificateFlags::SSL_CERTIFICATE_UNKNOWN_CA;
     case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE : // the certificate signature could not be decrypted. This means that the actual signature value could not be determined rather than it not matching the expected value, this is only meaningful for RSA keys.
     case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE : // the CRL signature could not be decrypted: this means that the actual signature value could not be determined rather than it not matching the expected value. Unused.
     case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY : // the public key in the certificate SubjectPublicKeyInfo could not be read.
@@ -113,13 +132,13 @@ unsigned sslCertificateFlag(const unsigned& sslError)
     case X509_V_ERR_CERT_REJECTED : // the root CA is marked to reject the specified purpose.
     case X509_V_ERR_NO_EXPLICIT_POLICY : // the verification flags were set to require and explicit policy but none was present.
     case X509_V_ERR_DIFFERENT_CRL_SCOPE : // the only CRLs that could be found did not match the scope of the certificate.
-        return SSL_CERTIFICATE_INSECURE;
+        return SSLCertificateFlags::SSL_CERTIFICATE_INSECURE;
     case X509_V_ERR_CERT_NOT_YET_VALID : // the certificate is not yet valid: the notBefore date is after the current time.
     case X509_V_ERR_CRL_NOT_YET_VALID : // the CRL is not yet valid.
-        return SSL_CERTIFICATE_NOT_ACTIVATED;
+        return SSLCertificateFlags::SSL_CERTIFICATE_NOT_ACTIVATED;
     case X509_V_ERR_CERT_HAS_EXPIRED : // the certificate has expired: that is the notAfter date is before the current time.
     case X509_V_ERR_CRL_HAS_EXPIRED : // the CRL has expired.
-        return SSL_CERTIFICATE_EXPIRED;
+        return SSLCertificateFlags::SSL_CERTIFICATE_EXPIRED;
     case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD : // the certificate notBefore field contains an invalid time.
     case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD : // the certificate notAfter field contains an invalid time.
     case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD : // the CRL lastUpdate field contains an invalid time.
@@ -137,97 +156,18 @@ unsigned sslCertificateFlag(const unsigned& sslError)
     case X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX : // the format of the name constraint is not recognised: for example an email address format of a form not mentioned in RFC3280. This could be caused by a garbage extension or some new feature not currently supported.
     case X509_V_ERR_CRL_PATH_VALIDATION_ERROR : // an error occured when attempting to verify the CRL path. This error can only happen if extended CRL checking is enabled.
     case X509_V_ERR_APPLICATION_VERIFICATION : // an application specific error. This will never be returned unless explicitly set by an application.
-        return SSL_CERTIFICATE_GENERIC_ERROR;
+        return SSLCertificateFlags::SSL_CERTIFICATE_GENERIC_ERROR;
     case X509_V_ERR_CERT_REVOKED : // the certificate has been revoked.
-        return SSL_CERTIFICATE_REVOKED;
+        return SSLCertificateFlags::SSL_CERTIFICATE_REVOKED;
     case X509_V_ERR_INVALID_CA : // a CA certificate is invalid. Either it is not a CA or its extensions are not consistent with the supplied purpose.
     case X509_V_ERR_SUBJECT_ISSUER_MISMATCH : // the current candidate issuer certificate was rejected because its subject name did not match the issuer name of the current certificate. This is only set if issuer check debugging is enabled it is used for status notification and is not in itself an error.
     case X509_V_ERR_AKID_SKID_MISMATCH : // the current candidate issuer certificate was rejected because its subject key identifier was present and did not match the authority key identifier current certificate. This is only set if issuer check debugging is enabled it is used for status notification and is not in itself an error.
     case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH : // the current candidate issuer certificate was rejected because its issuer name and serial number was present and did not match the authority key identifier of the current certificate. This is only set if issuer check debugging is enabled it is used for status notification and is not in itself an error.
     case X509_V_ERR_KEYUSAGE_NO_CERTSIGN : // the current candidate issuer certificate was rejected because its keyUsage extension does not permit certificate signing. This is only set if issuer check debugging is enabled it is used for status notification and is not in itself an error.
-        return SSL_CERTIFICATE_BAD_IDENTITY;
+        return SSLCertificateFlags::SSL_CERTIFICATE_BAD_IDENTITY;
     default :
-        return SSL_CERTIFICATE_GENERIC_ERROR;
-    }
-}
-
-#if !PLATFORM(WIN)
-// success of certificates extraction
-bool pemData(X509_STORE_CTX* ctx, ListHashSet<String>& certificates)
-{
-    bool ok = true;
-    STACK_OF(X509)* certs = X509_STORE_CTX_get1_chain(ctx);
-    for (int i = 0; i < sk_X509_num(certs); i++) {
-        X509* uCert = sk_X509_value(certs, i);
-        BIO* bio = BIO_new(BIO_s_mem());
-        int res = PEM_write_bio_X509(bio, uCert);
-        if (!res) {
-            ok = false;
-            BIO_free(bio);
-            break;
-        }
-
-        unsigned char* certificateData;
-        long length = BIO_get_mem_data(bio, &certificateData);
-        if (length < 0) {
-            ok = false;
-            BIO_free(bio);
-            break;
-        }
-
-        certificateData[length] = '\0';
-        String certificate = certificateData;
-        certificates.add(certificate);
-        BIO_free(bio);
+        return SSLCertificateFlags::SSL_CERTIFICATE_GENERIC_ERROR;
     }
-        sk_X509_pop_free(certs, X509_free);
-        return ok;
-}
-#endif
-
-static int certVerifyCallback(int ok, X509_STORE_CTX* ctx)
-{
-    // whether the verification of the certificate in question was passed (preverify_ok=1) or not (preverify_ok=0)
-
-    unsigned err = X509_STORE_CTX_get_error(ctx);
-    if (!err)
-        return 1;
-
-    SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
-    SSL_CTX* sslctx = SSL_get_SSL_CTX(ssl);
-    CurlHandle* handle = reinterpret_cast<CurlHandle*>(SSL_CTX_get_app_data(sslctx));
-    String host = handle->getEffectiveURL().host();
-
-    handle->setSslErrors(sslCertificateFlag(err));
-
-#if PLATFORM(WIN)
-    auto it = allowedHosts().find(host);
-    ok = (it != allowedHosts().end());
-#else
-    ListHashSet<String> certificates;
-    if (!pemData(ctx, certificates))
-        return 0;
-    ok = sslIgnoreHTTPSCertificate(host, certificates);
-#endif
-
-    if (ok) {
-        // if the host and the certificate are stored for the current handle that means is enabled,
-        // so don't need to curl verifies the authenticity of the peer's certificate
-        handle->setSslVerifyPeer(CurlHandle::VerifyPeerDisable);
-    }
-    return ok;
-}
-
-static CURLcode sslctxfun(CURL* curl, void* sslctx, void* parm)
-{
-    SSL_CTX_set_app_data(reinterpret_cast<SSL_CTX*>(sslctx), parm);
-    SSL_CTX_set_verify(reinterpret_cast<SSL_CTX*>(sslctx), SSL_VERIFY_PEER, certVerifyCallback);
-    return CURLE_OK;
-}
-
-void setSSLVerifyOptions(CurlHandle& handle)
-{
-    handle.setSslCtxCallbackFunction(sslctxfun, &handle);
 }
 
 }
diff --git a/Source/WebCore/platform/network/curl/CurlSSLVerifier.h b/Source/WebCore/platform/network/curl/CurlSSLVerifier.h
new file mode 100644 (file)
index 0000000..8f381a4
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 University of Szeged
+ * Copyright (C) 2017 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if OS(WINDOWS)
+#include <winsock2.h>
+#endif
+
+#include <openssl/ssl.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CurlHandle;
+
+class CurlSSLVerifier {
+WTF_MAKE_NONCOPYABLE(CurlSSLVerifier);
+
+public:
+    enum class SSLCertificateFlags {
+        SSL_CERTIFICATE_UNKNOWN_CA = (1 << 0), // The signing certificate authority is not known.
+        SSL_CERTIFICATE_BAD_IDENTITY = (1 << 1), // The certificate does not match the expected identity of the site that it was retrieved from.
+        SSL_CERTIFICATE_NOT_ACTIVATED = (1 << 2), // The certificate's activation time is still in the future
+        SSL_CERTIFICATE_EXPIRED = (1 << 3), // The certificate has expired
+        SSL_CERTIFICATE_REVOKED = (1 << 4), // The certificate has been revoked
+        SSL_CERTIFICATE_INSECURE = (1 << 5), // The certificate's algorithm is considered insecure.
+        SSL_CERTIFICATE_GENERIC_ERROR = (1 << 6) // Some other error occurred validating the certificate
+    };
+
+    CurlSSLVerifier() = default;
+
+    void setCurlHandle(CurlHandle* curlHandle) { m_curlHandle = curlHandle; }
+    void setHostName(const String& hostName) { m_hostName = hostName; }
+    void setSslCtx(void*);
+
+    int sslErrors() { return m_sslErrors; }
+
+private:
+    static int certVerifyCallback(int, X509_STORE_CTX*);
+
+#if !OS(WINDOWS)
+    bool getPemDataFromCtx(X509_STORE_CTX*, ListHashSet<String>&);
+#endif
+
+    SSLCertificateFlags convertToSSLCertificateFlags(const unsigned&);
+
+    CurlHandle* m_curlHandle { nullptr };
+    String m_hostName;
+    int m_sslErrors { 0 };
+};
+
+}
index db9d53a..2a78cea 100644 (file)
 #include "CurlCacheManager.h"
 #include "CurlContext.h"
 #include "CurlJobManager.h"
+#include "CurlSSLHandle.h"
 #include "FileSystem.h"
 #include "Logging.h"
 #include "MIMETypeRegistry.h"
 #include "NetworkingContext.h"
 #include "ResourceHandleInternal.h"
-#include "SSLHandle.h"
 #include "SynchronousLoaderClient.h"
 #include <wtf/text/Base64.h>
 
@@ -87,7 +87,7 @@ void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
 {
     ASSERT(isMainThread());
 
-    allowsAnyHTTPSCertificateHosts(host);
+    CurlContext::singleton().sslHandle().setHostAllowsAnyHTTPSCertificate(host);
 }
 
 void ResourceHandle::setClientCertificateInfo(const String& host, const String& certificate, const String& key)
@@ -95,7 +95,7 @@ void ResourceHandle::setClientCertificateInfo(const String& host, const String&
     ASSERT(isMainThread());
 
     if (fileExists(certificate))
-        addAllowedClientCertificate(host, certificate, key);
+        CurlContext::singleton().sslHandle().setClientCertificateInfo(host, certificate, key);
     else
         LOG(Network, "Invalid client certificate file: %s!\n", certificate.latin1().data());
 }
index d0c623a..3d28616 100644 (file)
@@ -39,7 +39,6 @@
 #include "MultipartHandle.h"
 #include "ResourceHandle.h"
 #include "ResourceHandleInternal.h"
-#include "SSLHandle.h"
 #include "TextEncoding.h"
 #include "ThreadSafeDataBuffer.h"
 #include "URL.h"
@@ -78,8 +77,6 @@ ResourceHandleCurlDelegate::ResourceHandleCurlDelegate(ResourceHandle* handle)
         }
     }
 
-    m_sslClientCertificate = getSSLClientCertificate(url.host());
-
     setupAuthentication();
 }
 
@@ -214,6 +211,8 @@ void ResourceHandleCurlDelegate::setupRequest()
     m_curlHandle.enableStdErrIfUsed();
 #endif
 
+    auto& sslHandle = CurlContext::singleton().sslHandle();
+
     m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerEnable);
     m_curlHandle.setSslVerifyHost(CurlHandle::VerifyHostStrictNameCheck);
     m_curlHandle.setPrivateData(this);
@@ -226,18 +225,19 @@ void ResourceHandleCurlDelegate::setupRequest()
     m_curlHandle.enableTimeout();
     m_curlHandle.enableAllowedProtocols();
 
-    if (m_sslClientCertificate) {
-        m_curlHandle.setSslCert((*m_sslClientCertificate).first.utf8().data());
+    auto sslClientCertificate = sslHandle.getSSLClientCertificate(url.host());
+    if (sslClientCertificate) {
+        m_curlHandle.setSslCert(sslClientCertificate->first.utf8().data());
         m_curlHandle.setSslCertType("P12");
-        m_curlHandle.setSslKeyPassword((*m_sslClientCertificate).second.utf8().data());
+        m_curlHandle.setSslKeyPassword(sslClientCertificate->second.utf8().data());
     }
 
-    if (CurlContext::singleton().shouldIgnoreSSLErrors())
+    if (sslHandle.shouldIgnoreSSLErrors())
         m_curlHandle.setSslVerifyPeer(CurlHandle::VerifyPeerDisable);
     else
-        setSSLVerifyOptions(m_curlHandle);
+        m_curlHandle.setSslCtxCallbackFunction(willSetupSslCtxCallback, this);
 
-    m_curlHandle.enableCAInfoIfExists();
+    m_curlHandle.setCACertPath(sslHandle.getCACertPath());
 
     m_curlHandle.enableAcceptEncoding();
     m_curlHandle.setUrl(urlString);
@@ -288,7 +288,7 @@ void ResourceHandleCurlDelegate::notifyFail()
     int errorCode = m_curlHandle.errorCode();
     URL failingURL = m_curlHandle.getEffectiveURL();
     String errorDescription = m_curlHandle.errorDescription();
-    unsigned sslErrors = m_curlHandle.getSslErrors();
+    unsigned sslErrors = m_sslVerifier.sslErrors();
 
     if (isMainThread())
         didFail(domain, errorCode, failingURL, errorDescription, sslErrors);
@@ -712,6 +712,15 @@ NetworkLoadMetrics ResourceHandleCurlDelegate::getNetworkLoadMetrics()
     return networkLoadMetrics;
 }
 
+CURLcode ResourceHandleCurlDelegate::willSetupSslCtx(void* sslCtx)
+{
+    m_sslVerifier.setCurlHandle(&m_curlHandle);
+    m_sslVerifier.setHostName(m_firstRequest.url().host());
+    m_sslVerifier.setSslCtx(sslCtx);
+
+    return CURLE_OK;
+}
+
 /*
 * This is being called for each HTTP header in the response. This includes '\r\n'
 * for the last line of the header.
@@ -856,6 +865,11 @@ size_t ResourceHandleCurlDelegate::willSendData(char* buffer, size_t blockSize,
     return m_sendBytes;
 }
 
+CURLcode ResourceHandleCurlDelegate::willSetupSslCtxCallback(CURL*, void* sslCtx, void* userData)
+{
+    return static_cast<ResourceHandleCurlDelegate*>(userData)->willSetupSslCtx(sslCtx);
+}
+
 size_t ResourceHandleCurlDelegate::didReceiveHeaderCallback(char* ptr, size_t blockSize, size_t numberOfBlocks, void* data)
 {
     return static_cast<ResourceHandleCurlDelegate*>(const_cast<void*>(data))->didReceiveHeader(String(static_cast<const char*>(ptr), blockSize * numberOfBlocks));
index 167a378..7fe2e09 100644 (file)
 #include "Credential.h"
 #include "CurlContext.h"
 #include "CurlJobManager.h"
+#include "CurlSSLVerifier.h"
 #include "FormDataStreamCurl.h"
 #include "ResourceRequest.h"
 #include "ResourceResponse.h"
-#include "SSLHandle.h"
 #include <wtf/Condition.h>
 #include <wtf/ThreadSafeRefCounted.h>
 
@@ -93,10 +93,12 @@ private:
     void applyAuthentication();
     NetworkLoadMetrics getNetworkLoadMetrics();
 
+    CURLcode willSetupSslCtx(void*);
     size_t didReceiveHeader(String&&);
     size_t didReceiveData(ThreadSafeDataBuffer);
     size_t willSendData(char*, size_t blockSize, size_t numberOfBlocks);
 
+    static CURLcode willSetupSslCtxCallback(CURL*, void*, void*);
     static size_t didReceiveHeaderCallback(char*, size_t blockSize, size_t numberOfBlocks, void*);
     static size_t didReceiveDataCallback(char*, size_t blockSize, size_t numberOfBlocks, void*);
     static size_t willSendDataCallback(char*, size_t blockSize, size_t numberOfBlocks, void*);
@@ -114,11 +116,11 @@ private:
     String m_user;
     String m_pass;
     Credential m_initialCredential;
-    std::optional<ClientCertificate> m_sslClientCertificate;
     bool m_defersLoading;
     bool m_addedCacheValidationHeaders { false };
     Vector<char> m_postBytes;
     CurlHandle m_curlHandle;
+    CurlSSLVerifier m_sslVerifier;
     // Used by both threads.
     Condition m_workerThreadConditionVariable;
     Lock m_workerThreadMutex;