[Curl] Embed certificate information into ResourceResponse.
authorBasuke.Suzuki@sony.com <Basuke.Suzuki@sony.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Jul 2018 21:38:22 +0000 (21:38 +0000)
committerBasuke.Suzuki@sony.com <Basuke.Suzuki@sony.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Jul 2018 21:38:22 +0000 (21:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=187102

Reviewed by Youenn Fablet.

Source/WebCore:

No new tests but tested internally.

Collect certificate information from the communication and put them into
ResourceResponse for the sake of advanced security checking and information
providing to the user.

* platform/Curl.cmake:
* platform/network/curl/CertificateInfo.cpp: Copied from Source/WebCore/platform/network/curl/CertificateInfo.h.
(WebCore::CertificateInfo::CertificateInfo):
(WebCore::CertificateInfo::isolatedCopy const):
(WebCore::CertificateInfo::makeCertificate):
* platform/network/curl/CertificateInfo.h:
(WebCore::CertificateInfo::verificationError const):
(WebCore::CertificateInfo::certificateChain const):
(WebCore::operator==):
(WebCore::CertificateInfo::CertificateInfo): Deleted.
* platform/network/curl/CurlRequest.cpp:
(WebCore::CurlRequest::didReceiveHeader):
(WebCore::CurlRequest::didCompleteTransfer):
* platform/network/curl/CurlRequest.h:
(WebCore::CurlRequest::getCertificateInfo const):
(WebCore::CurlRequest::getNetworkLoadMetrics const):
(WebCore::CurlRequest::getNetworkLoadMetrics): Deleted.
* platform/network/curl/CurlResourceHandleDelegate.cpp:
(WebCore::CurlResourceHandleDelegate::curlDidReceiveResponse):
(WebCore::CurlResourceHandleDelegate::curlDidComplete):
* platform/network/curl/CurlSSLHandle.cpp:
(WebCore::CurlSSLHandle::setHostAllowsAnyHTTPSCertificate):
(WebCore::CurlSSLHandle::canIgnoredHTTPSCertificate):
* platform/network/curl/CurlSSLHandle.h:
* platform/network/curl/CurlSSLVerifier.cpp:
(WebCore::CurlSSLVerifier::CurlSSLVerifier):
(WebCore::CurlSSLVerifier::verifyCallback):
(WebCore::StackOfX509::StackOfX509): For RAII. Used in pemDataFromCtx().
(WebCore::StackOfX509::~StackOfX509): Ditto.
(WebCore::StackOfX509::count): Ditto.
(WebCore::StackOfX509::item): Ditto.
(WebCore::BIOHolder::BIOHolder): Ditto.
(WebCore::BIOHolder::~BIOHolder): Ditto.
(WebCore::BIOHolder::write): Ditto.
(WebCore::BIOHolder::asCertificate): Ditto.
(WebCore::pemDataFromCtx): Become simple static function.
(WebCore::convertToSSLCertificateFlags): Ditto.
(WebCore::CurlSSLVerifier::certVerifyCallback): Deleted.
(WebCore::CurlSSLVerifier::getPemDataFromCtx): Deleted.
(WebCore::CurlSSLVerifier::convertToSSLCertificateFlags): Deleted.
* platform/network/curl/CurlSSLVerifier.h:
(WebCore::CurlSSLVerifier::verificationError):
(WebCore::CurlSSLVerifier::certificateChain const):
* platform/network/curl/ResourceError.h:
* platform/network/curl/ResourceErrorCurl.cpp:
(WebCore::ResourceError::isSSLConnectError const):
(WebCore::ResourceError::isSSLCertVerificationError const):
(WebCore::ResourceError::hasSSLConnectError const): Deleted.
* platform/network/curl/ResourceResponse.h:
* platform/network/curl/ResourceResponseCurl.cpp:
(WebCore::ResourceResponse::setCertificateInfo):
(WebCore::ResourceResponse::setDeprecatedNetworkLoadMetrics):

Source/WebKit:

* NetworkProcess/curl/NetworkDataTaskCurl.cpp:
(WebKit::NetworkDataTaskCurl::curlDidComplete):.

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

17 files changed:
Source/WebCore/ChangeLog
Source/WebCore/platform/Curl.cmake
Source/WebCore/platform/network/curl/CertificateInfo.cpp [new file with mode: 0644]
Source/WebCore/platform/network/curl/CertificateInfo.h
Source/WebCore/platform/network/curl/CurlRequest.cpp
Source/WebCore/platform/network/curl/CurlRequest.h
Source/WebCore/platform/network/curl/CurlResourceHandleDelegate.cpp
Source/WebCore/platform/network/curl/CurlSSLHandle.cpp
Source/WebCore/platform/network/curl/CurlSSLHandle.h
Source/WebCore/platform/network/curl/CurlSSLVerifier.cpp
Source/WebCore/platform/network/curl/CurlSSLVerifier.h
Source/WebCore/platform/network/curl/ResourceError.h
Source/WebCore/platform/network/curl/ResourceErrorCurl.cpp
Source/WebCore/platform/network/curl/ResourceResponse.h
Source/WebCore/platform/network/curl/ResourceResponseCurl.cpp
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/curl/NetworkDataTaskCurl.cpp

index 1b02f39..76297c1 100644 (file)
@@ -1,3 +1,69 @@
+2018-07-03  Basuke Suzuki  <Basuke.Suzuki@sony.com>
+
+        [Curl] Embed certificate information into ResourceResponse.
+        https://bugs.webkit.org/show_bug.cgi?id=187102
+
+        Reviewed by Youenn Fablet.
+
+        No new tests but tested internally.
+
+        Collect certificate information from the communication and put them into
+        ResourceResponse for the sake of advanced security checking and information
+        providing to the user.
+
+        * platform/Curl.cmake:
+        * platform/network/curl/CertificateInfo.cpp: Copied from Source/WebCore/platform/network/curl/CertificateInfo.h.
+        (WebCore::CertificateInfo::CertificateInfo):
+        (WebCore::CertificateInfo::isolatedCopy const):
+        (WebCore::CertificateInfo::makeCertificate):
+        * platform/network/curl/CertificateInfo.h:
+        (WebCore::CertificateInfo::verificationError const):
+        (WebCore::CertificateInfo::certificateChain const):
+        (WebCore::operator==):
+        (WebCore::CertificateInfo::CertificateInfo): Deleted.
+        * platform/network/curl/CurlRequest.cpp:
+        (WebCore::CurlRequest::didReceiveHeader):
+        (WebCore::CurlRequest::didCompleteTransfer):
+        * platform/network/curl/CurlRequest.h:
+        (WebCore::CurlRequest::getCertificateInfo const):
+        (WebCore::CurlRequest::getNetworkLoadMetrics const):
+        (WebCore::CurlRequest::getNetworkLoadMetrics): Deleted.
+        * platform/network/curl/CurlResourceHandleDelegate.cpp:
+        (WebCore::CurlResourceHandleDelegate::curlDidReceiveResponse):
+        (WebCore::CurlResourceHandleDelegate::curlDidComplete):
+        * platform/network/curl/CurlSSLHandle.cpp:
+        (WebCore::CurlSSLHandle::setHostAllowsAnyHTTPSCertificate):
+        (WebCore::CurlSSLHandle::canIgnoredHTTPSCertificate):
+        * platform/network/curl/CurlSSLHandle.h:
+        * platform/network/curl/CurlSSLVerifier.cpp:
+        (WebCore::CurlSSLVerifier::CurlSSLVerifier):
+        (WebCore::CurlSSLVerifier::verifyCallback):
+        (WebCore::StackOfX509::StackOfX509): For RAII. Used in pemDataFromCtx().
+        (WebCore::StackOfX509::~StackOfX509): Ditto.
+        (WebCore::StackOfX509::count): Ditto.
+        (WebCore::StackOfX509::item): Ditto.
+        (WebCore::BIOHolder::BIOHolder): Ditto.
+        (WebCore::BIOHolder::~BIOHolder): Ditto.
+        (WebCore::BIOHolder::write): Ditto.
+        (WebCore::BIOHolder::asCertificate): Ditto.
+        (WebCore::pemDataFromCtx): Become simple static function.
+        (WebCore::convertToSSLCertificateFlags): Ditto.
+        (WebCore::CurlSSLVerifier::certVerifyCallback): Deleted.
+        (WebCore::CurlSSLVerifier::getPemDataFromCtx): Deleted.
+        (WebCore::CurlSSLVerifier::convertToSSLCertificateFlags): Deleted.
+        * platform/network/curl/CurlSSLVerifier.h:
+        (WebCore::CurlSSLVerifier::verificationError):
+        (WebCore::CurlSSLVerifier::certificateChain const):
+        * platform/network/curl/ResourceError.h:
+        * platform/network/curl/ResourceErrorCurl.cpp:
+        (WebCore::ResourceError::isSSLConnectError const):
+        (WebCore::ResourceError::isSSLCertVerificationError const):
+        (WebCore::ResourceError::hasSSLConnectError const): Deleted.
+        * platform/network/curl/ResourceResponse.h:
+        * platform/network/curl/ResourceResponseCurl.cpp:
+        (WebCore::ResourceResponse::setCertificateInfo):
+        (WebCore::ResourceResponse::setDeprecatedNetworkLoadMetrics):
+
 2018-07-02  Simon Fraser  <simon.fraser@apple.com>
 
         Clean up the layer volatility code and logging
index 1c27796..1051130 100644 (file)
@@ -4,6 +4,7 @@ list(APPEND WebCore_INCLUDE_DIRECTORIES
 
 list(APPEND WebCore_SOURCES
     platform/network/curl/AuthenticationChallengeCurl.cpp
+    platform/network/curl/CertificateInfo.cpp
     platform/network/curl/CookieJarCurl.cpp
     platform/network/curl/CookieJarCurlDatabase.cpp
     platform/network/curl/CookieJarDB.cpp
diff --git a/Source/WebCore/platform/network/curl/CertificateInfo.cpp b/Source/WebCore/platform/network/curl/CertificateInfo.cpp
new file mode 100644 (file)
index 0000000..dc689f7
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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 APPLE INC. AND ITS CONTRIBUTORS ``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 ITS 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 "CertificateInfo.h"
+
+#include <wtf/CrossThreadCopier.h>
+
+#if USE(CURL)
+
+namespace WebCore {
+
+CertificateInfo::CertificateInfo(int verificationError, Vector<Certificate>&& certificateChain)
+    : m_verificationError(verificationError)
+    , m_certificateChain(WTFMove(certificateChain))
+{
+}
+
+CertificateInfo CertificateInfo::isolatedCopy() const
+{
+    return { m_verificationError, crossThreadCopy(m_certificateChain) };
+}
+
+CertificateInfo::Certificate CertificateInfo::makeCertificate(const char* buffer, size_t size)
+{
+    Certificate certificate;
+    certificate.append(buffer, size);
+    return certificate;
+}
+
+}
+
+#endif
index 798bf1e..4705f3b 100644 (file)
 #pragma once
 
 #include "NotImplemented.h"
+#include <wtf/Vector.h>
 
 namespace WebCore {
 
 class CertificateInfo {
 public:
-    CertificateInfo() { }
+    using Certificate = Vector<char>;
+
+    CertificateInfo() = default;
+    WEBCORE_EXPORT CertificateInfo(int verificationError, Vector<Certificate>&&);
+
+    WEBCORE_EXPORT CertificateInfo isolatedCopy() const;
+
+    int verificationError() const { return m_verificationError; }
+    const Vector<Certificate>& certificateChain() const { return m_certificateChain; }
 
     bool containsNonRootSHA1SignedCertificate() const { notImplemented(); return false; }
+
+    static Certificate makeCertificate(const char*, size_t);
+
+private:
+    int m_verificationError { 0 };
+    Vector<Certificate> m_certificateChain;
 };
 
+inline bool operator==(const CertificateInfo& a, const CertificateInfo& b)
+{
+    return a.verificationError() == b.verificationError() && a.certificateChain() == b.certificateChain();
+}
+
 } // namespace WebCore
index b6b5dfd..0bdae94 100644 (file)
@@ -352,6 +352,9 @@ size_t CurlRequest::didReceiveHeader(String&& header)
     if (m_response.availableProxyAuth)
         CurlContext::singleton().setProxyAuthMethod(m_response.availableProxyAuth);
 
+    if (m_sslVerifier)
+        m_certificateInfo = m_sslVerifier->certificateInfo();
+
     if (m_enableMultipart)
         m_multipartHandle = CurlMultipartHandle::createIfNeeded(*this, m_response);
 
@@ -460,9 +463,12 @@ void CurlRequest::didCompleteTransfer(CURLcode result)
     } else {
         auto type = (result == CURLE_OPERATION_TIMEDOUT && m_request.timeoutInterval() > 0.0) ? ResourceError::Type::Timeout : ResourceError::Type::General;
         auto resourceError = ResourceError::httpError(result, m_request.url(), type);
-        if (m_sslVerifier && m_sslVerifier->sslErrors())
+        if (m_sslVerifier) {
             resourceError.setSslErrors(m_sslVerifier->sslErrors());
 
+            m_certificateInfo = m_sslVerifier->certificateInfo();
+        }
+
         finalizeTransfer();
         callClient([error = resourceError.isolatedCopy()](CurlRequest& request, CurlRequestClient& client) {
             client.curlDidFailWithError(request, error);
index e9e1021..59d80d7 100644 (file)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "CertificateInfo.h"
 #include "CurlFormDataStream.h"
 #include "CurlMultipartHandle.h"
 #include "CurlMultipartHandleClient.h"
@@ -87,7 +88,8 @@ public:
     void enableDownloadToFile();
     const String& getDownloadedFilePath();
 
-    NetworkLoadMetrics getNetworkLoadMetrics() { return m_networkLoadMetrics.isolatedCopy(); }
+    const CertificateInfo& certificateInfo() const { return m_certificateInfo; }
+    const NetworkLoadMetrics& networkLoadMetrics() const { return m_networkLoadMetrics; }
 
 private:
     enum class Action {
@@ -193,6 +195,7 @@ private:
     String m_downloadFilePath;
     FileSystem::PlatformFileHandle m_downloadFileHandle { FileSystem::invalidPlatformFileHandle };
 
+    CertificateInfo m_certificateInfo;
     NetworkLoadMetrics m_networkLoadMetrics;
 };
 
index 416b968..6a06f19 100644 (file)
@@ -108,7 +108,9 @@ void CurlResourceHandleDelegate::curlDidReceiveResponse(CurlRequest& request, co
         return;
 
     m_response = ResourceResponse(receivedResponse);
-    m_response.setDeprecatedNetworkLoadMetrics(request.getNetworkLoadMetrics());
+
+    m_response.setCertificateInfo(request.certificateInfo().isolatedCopy());
+    m_response.setDeprecatedNetworkLoadMetrics(request.networkLoadMetrics().isolatedCopy());
 
     handleCookieHeaders(receivedResponse);
 
@@ -162,7 +164,7 @@ void CurlResourceHandleDelegate::curlDidComplete(CurlRequest& request)
     if (cancelledOrClientless())
         return;
 
-    m_response.setDeprecatedNetworkLoadMetrics(request.getNetworkLoadMetrics());
+    m_response.setDeprecatedNetworkLoadMetrics(request.networkLoadMetrics().isolatedCopy());
 
     CurlCacheManager::singleton().didFinishLoading(m_handle);
     client()->didFinishLoading(&m_handle);
index 4c5c0e8..8574bb0 100644 (file)
@@ -81,7 +81,7 @@ void CurlSSLHandle::setCACertPath(String&& caCertPath)
     m_caCertInfo = WTFMove(caCertPath);
 }
 
-void CurlSSLHandle::setCACertData(Vector<char>&& caCertData)
+void CurlSSLHandle::setCACertData(CertificateInfo::Certificate&& caCertData)
 {
     RELEASE_ASSERT(!caCertData.isEmpty());
     m_caCertInfo = WTFMove(caCertData);
@@ -96,8 +96,7 @@ void CurlSSLHandle::setHostAllowsAnyHTTPSCertificate(const String& hostName)
 {
     LockHolder mutex(m_mutex);
 
-    ListHashSet<String> certificates;
-    m_allowedHosts.set(hostName, certificates);
+    m_allowedHosts.set(hostName, Vector<CertificateInfo::Certificate> { });
 }
 
 bool CurlSSLHandle::isAllowedHTTPSCertificateHost(const String& hostName)
@@ -108,7 +107,7 @@ bool CurlSSLHandle::isAllowedHTTPSCertificateHost(const String& hostName)
     return (it != m_allowedHosts.end());
 }
 
-bool CurlSSLHandle::canIgnoredHTTPSCertificate(const String& hostName, const ListHashSet<String>& certificates)
+bool CurlSSLHandle::canIgnoredHTTPSCertificate(const String& hostName, const Vector<CertificateInfo::Certificate>& certificates)
 {
     LockHolder mutex(m_mutex);
 
@@ -129,8 +128,7 @@ void CurlSSLHandle::setClientCertificateInfo(const String& hostName, const Strin
 {
     LockHolder mutex(m_mutex);
 
-    ClientCertificate clientInfo(certificate, key);
-    m_allowedClientHosts.set(hostName, clientInfo);
+    m_allowedClientHosts.set(hostName, ClientCertificate { certificate, key });
 }
 
 std::optional<CurlSSLHandle::ClientCertificate> CurlSSLHandle::getSSLClientCertificate(const String& hostName)
index 6d57f53..f515de4 100644 (file)
@@ -26,9 +26,9 @@
 
 #pragma once
 
+#include "CertificateInfo.h"
 #include <openssl/crypto.h>
 #include <wtf/HashMap.h>
-#include <wtf/ListHashSet.h>
 #include <wtf/NeverDestroyed.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/Variant.h>
@@ -49,7 +49,7 @@ class CurlSSLHandle {
     using ClientCertificate = std::pair<String, String>;
 
 public:
-    using CACertInfo = Variant<Monostate, String, Vector<char>>;
+    using CACertInfo = Variant<Monostate, String, CertificateInfo::Certificate>;
 
     CurlSSLHandle();
 
@@ -66,12 +66,12 @@ public:
 
     const CACertInfo& getCACertInfo() const { return m_caCertInfo; }
     WEBCORE_EXPORT void setCACertPath(String&&);
-    WEBCORE_EXPORT void setCACertData(Vector<char>&&);
+    WEBCORE_EXPORT void setCACertData(CertificateInfo::Certificate&&);
     WEBCORE_EXPORT void clearCACertInfo();
 
     WEBCORE_EXPORT void setHostAllowsAnyHTTPSCertificate(const String&);
     bool isAllowedHTTPSCertificateHost(const String&);
-    bool canIgnoredHTTPSCertificate(const String&, const ListHashSet<String>&);
+    bool canIgnoredHTTPSCertificate(const String&, const Vector<CertificateInfo::Certificate>&);
 
     WEBCORE_EXPORT void setClientCertificateInfo(const String&, const String&, const String&);
     std::optional<ClientCertificate> getSSLClientCertificate(const String&);
@@ -114,7 +114,7 @@ private:
     bool m_ignoreSSLErrors { false };
 
     Lock m_mutex;
-    HashMap<String, ListHashSet<String>, ASCIICaseInsensitiveHash> m_allowedHosts;
+    HashMap<String, Vector<CertificateInfo::Certificate>, ASCIICaseInsensitiveHash> m_allowedHosts;
     HashMap<String, ClientCertificate, ASCIICaseInsensitiveHash> m_allowedClientHosts;
 };
 
index 3209c8b..a47cf8f 100644 (file)
 
 namespace WebCore {
 
+static Vector<CertificateInfo::Certificate> pemDataFromCtx(X509_STORE_CTX*);
+static CurlSSLVerifier::SSLCertificateFlags convertToSSLCertificateFlags(unsigned);
+
 CurlSSLVerifier::CurlSSLVerifier(CurlHandle* curlHandle, const String& hostName, void* sslCtx)
     : m_curlHandle(curlHandle)
     , m_hostName(hostName)
 {
     auto* ctx = static_cast<SSL_CTX*>(sslCtx);
-    auto& sslHandle = CurlContext::singleton().sslHandle();
+    const auto& sslHandle = CurlContext::singleton().sslHandle();
 
     SSL_CTX_set_app_data(ctx, this);
-    SSL_CTX_set_verify(ctx, SSL_CTX_get_verify_mode(ctx), certVerifyCallback);
+    SSL_CTX_set_verify(ctx, SSL_CTX_get_verify_mode(ctx), verifyCallback);
 
 #if defined(LIBRESSL_VERSION_NUMBER)
     if (auto data = WTF::get_if<Vector<char>>(sslHandle.getCACertInfo()))
@@ -50,94 +53,129 @@ CurlSSLVerifier::CurlSSLVerifier(CurlHandle* curlHandle, const String& hostName,
 #endif
 
 #if (!defined(LIBRESSL_VERSION_NUMBER))
-    auto signatureAlgorithmsList = sslHandle.getSignatureAlgorithmsList();
+    const auto& signatureAlgorithmsList = sslHandle.getSignatureAlgorithmsList();
     if (!signatureAlgorithmsList.isEmpty())
         SSL_CTX_set1_sigalgs_list(ctx, signatureAlgorithmsList->utf8().data());
 #endif
 
-    auto curvesList = sslHandle.getCurvesList();
+    const auto& curvesList = sslHandle.getCurvesList();
     if (!curvesList.isEmpty())
         SSL_CTX_set1_curves_list(ctx, curvesList.utf8().data());
 }
 
-int CurlSSLVerifier::certVerifyCallback(int ok, X509_STORE_CTX* storeCtx)
+bool CurlSSLVerifier::verify(X509_STORE_CTX* ctx)
 {
     // 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;
+    m_certificateInfo = CertificateInfo { X509_STORE_CTX_get_error(ctx), pemDataFromCtx(ctx) };
 
-    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;
+    auto error = m_certificateInfo.verificationError();
+    if (!error)
+        return 1;
 
-    verifier->m_sslErrors = static_cast<int>(verifier->convertToSSLCertificateFlags(certErrCd));
+    m_sslErrors = static_cast<int>(convertToSSLCertificateFlags(error));
 
 #if PLATFORM(WIN)
-    ok = CurlContext::singleton().sslHandle().isAllowedHTTPSCertificateHost(verifier->m_hostName);
+    bool ok = CurlContext::singleton().sslHandle().isAllowedHTTPSCertificateHost(m_hostName);
 #else
-    ListHashSet<String> certificates;
-    if (!getPemDataFromCtx(storeCtx, certificates))
-        return 0;
-
-    ok = CurlContext::singleton().sslHandle().canIgnoredHTTPSCertificate(verifier->m_hostName, certificates);
+    bool ok = CurlContext::singleton().sslHandle().canIgnoredHTTPSCertificate(m_hostName, m_certificateInfo.certificateChain());
 #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
-        if (verifier->m_curlHandle)
-            verifier->m_curlHandle->setSslVerifyPeer(CurlHandle::VerifyPeer::Disable);
+        if (m_curlHandle)
+            m_curlHandle->setSslVerifyPeer(CurlHandle::VerifyPeer::Disable);
     }
 
     return ok;
 }
 
-#if !PLATFORM(WIN)
+int CurlSSLVerifier::verifyCallback(int ok, X509_STORE_CTX* ctx)
+{
+    auto ssl = static_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
+    auto sslCtx = SSL_get_SSL_CTX(ssl);
+    auto verifier = static_cast<CurlSSLVerifier*>(SSL_CTX_get_app_data(sslCtx));
+
+    // whether the verification of the certificate in question was passed (preverify_ok=1) or not (preverify_ok=0)
+    return verifier->verify(ctx);
+}
+
+class StackOfX509 {
+public:
+    explicit StackOfX509(X509_STORE_CTX* ctx)
+        : m_certs { X509_STORE_CTX_get1_chain(ctx) }
+    {
+    }
+
+    ~StackOfX509()
+    {
+        if (m_certs)
+            sk_X509_pop_free(m_certs, X509_free);
+    }
+
+    unsigned count() { return sk_X509_num(m_certs); }
+    X509* item(unsigned i) { return sk_X509_value(m_certs, i); }
+
+private:
+    STACK_OF(X509)* m_certs { nullptr };
+};
 
-bool CurlSSLVerifier::getPemDataFromCtx(X509_STORE_CTX* ctx, ListHashSet<String>& certificates)
+class BIOHolder {
+public:
+    BIOHolder()
+        : m_bio { BIO_new(BIO_s_mem()) }
+    {
+    }
+
+    ~BIOHolder()
+    {
+        if (m_bio)
+            BIO_free(m_bio);
+    }
+
+    bool write(X509* data) { return PEM_write_bio_X509(m_bio, data); }
+    CertificateInfo::Certificate asCertificate()
+    {
+        char* data;
+        long length = BIO_get_mem_data(m_bio, &data);
+        if (length < 0)
+            return CertificateInfo::Certificate();
+
+        auto cert = CertificateInfo::makeCertificate(data, length);
+        return cert;
+    }
+
+private:
+    BIO* m_bio { nullptr };
+};
+
+static Vector<CertificateInfo::Certificate> pemDataFromCtx(X509_STORE_CTX* ctx)
 {
-    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);
+    Vector<CertificateInfo::Certificate> result;
+    StackOfX509 certs { ctx };
+    for (int i = 0; i < certs.count(); i++) {
+        BIOHolder bio;
+
+        if (!bio.write(certs.item(i)))
+            return Vector<CertificateInfo::Certificate> { };
+
+        auto certificate = bio.asCertificate();
+        if (certificate.isEmpty())
+            return Vector<CertificateInfo::Certificate> { };
+
+        result.append(WTFMove(certificate));
     }
 
-    sk_X509_pop_free(certs, X509_free);
-    return ok;
+    return result;
 }
 
-#endif
-
-CurlSSLVerifier::SSLCertificateFlags CurlSSLVerifier::convertToSSLCertificateFlags(const unsigned& sslError)
+static CurlSSLVerifier::SSLCertificateFlags convertToSSLCertificateFlags(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 SSLCertificateFlags::SSL_CERTIFICATE_UNKNOWN_CA;
+        return CurlSSLVerifier::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.
@@ -150,13 +188,13 @@ CurlSSLVerifier::SSLCertificateFlags CurlSSLVerifier::convertToSSLCertificateFla
     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 SSLCertificateFlags::SSL_CERTIFICATE_INSECURE;
+        return CurlSSLVerifier::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 SSLCertificateFlags::SSL_CERTIFICATE_NOT_ACTIVATED;
+        return CurlSSLVerifier::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 SSLCertificateFlags::SSL_CERTIFICATE_EXPIRED;
+        return CurlSSLVerifier::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.
@@ -174,17 +212,17 @@ CurlSSLVerifier::SSLCertificateFlags CurlSSLVerifier::convertToSSLCertificateFla
     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 SSLCertificateFlags::SSL_CERTIFICATE_GENERIC_ERROR;
+        return CurlSSLVerifier::SSLCertificateFlags::SSL_CERTIFICATE_GENERIC_ERROR;
     case X509_V_ERR_CERT_REVOKED : // the certificate has been revoked.
-        return SSLCertificateFlags::SSL_CERTIFICATE_REVOKED;
+        return CurlSSLVerifier::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 SSLCertificateFlags::SSL_CERTIFICATE_BAD_IDENTITY;
+        return CurlSSLVerifier::SSLCertificateFlags::SSL_CERTIFICATE_BAD_IDENTITY;
     default :
-        return SSLCertificateFlags::SSL_CERTIFICATE_GENERIC_ERROR;
+        return CurlSSLVerifier::SSLCertificateFlags::SSL_CERTIFICATE_GENERIC_ERROR;
     }
 }
 
index 6ec536c..48a6543 100644 (file)
@@ -26,7 +26,7 @@
 
 #pragma once
 
-#include <wtf/ListHashSet.h>
+#include "CertificateInfo.h"
 #include <wtf/Noncopyable.h>
 #include <wtf/text/WTFString.h>
 
@@ -53,19 +53,18 @@ public:
     CurlSSLVerifier(CurlHandle*, const String& hostName, void* sslCtx);
 
     int sslErrors() { return m_sslErrors; }
+    const CertificateInfo& certificateInfo() const { return m_certificateInfo; }
 
 private:
-    static int certVerifyCallback(int, X509_STORE_CTX*);
-
-#if !PLATFORM(WIN)
-    static bool getPemDataFromCtx(X509_STORE_CTX*, ListHashSet<String>&);
-#endif
-
-    SSLCertificateFlags convertToSSLCertificateFlags(const unsigned&);
+    static int verifyCallback(int, X509_STORE_CTX*);
 
     CurlHandle* m_curlHandle { };
     String m_hostName;
+
     int m_sslErrors { 0 };
+    CertificateInfo m_certificateInfo;
+
+    bool verify(X509_STORE_CTX*);
 };
 
 } // namespace WebCore
index 3dbb0d2..f30ef3b 100644 (file)
@@ -49,7 +49,9 @@ public:
 
     unsigned sslErrors() const { return m_sslErrors; }
     void setSslErrors(unsigned sslErrors) { m_sslErrors = sslErrors; }
-    bool hasSSLConnectError() const;
+
+    bool isSSLConnectError() const;
+    WEBCORE_EXPORT bool isSSLCertVerificationError() const;
 
     static bool platformCompare(const ResourceError& a, const ResourceError& b);
 
index ea57e95..6416fa8 100644 (file)
@@ -46,11 +46,16 @@ ResourceError ResourceError::sslError(int errorCode, unsigned sslErrors, const U
     return resourceError;
 }
 
-bool ResourceError::hasSSLConnectError() const
+bool ResourceError::isSSLConnectError() const
 {
     return errorCode() == CURLE_SSL_CONNECT_ERROR;
 }
 
+bool ResourceError::isSSLCertVerificationError() const
+{
+    return errorCode() == CURLE_SSL_CACERT || errorCode() == CURLE_PEER_FAILED_VERIFICATION;
+}
+
 void ResourceError::doPlatformIsolatedCopy(const ResourceError& other)
 {
     m_sslErrors = other.m_sslErrors;
index 4fa04e4..7f5788f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2006 Apple Inc.  All rights reserved.
- * Copyright (C) 2017 Sony Interactive Entertainment Inc.
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -50,7 +50,8 @@ public:
 
     void appendHTTPHeaderField(const String&);
 
-    void setDeprecatedNetworkLoadMetrics(const NetworkLoadMetrics& networkLoadMetrics) { m_networkLoadMetrics = networkLoadMetrics; }
+    void setCertificateInfo(CertificateInfo&&);
+    void setDeprecatedNetworkLoadMetrics(NetworkLoadMetrics&&);
 
     bool shouldRedirect();
     bool isMovedPermanently() const;
index ac45ba2..02f30f6 100644 (file)
@@ -142,6 +142,16 @@ void ResourceResponse::setStatusLine(const String& header)
     }
 }
 
+void ResourceResponse::setCertificateInfo(CertificateInfo&& certificateInfo)
+{
+    m_certificateInfo = WTFMove(certificateInfo);
+}
+
+void ResourceResponse::setDeprecatedNetworkLoadMetrics(NetworkLoadMetrics&& networkLoadMetrics)
+{
+    m_networkLoadMetrics = WTFMove(networkLoadMetrics);
+}
+
 String ResourceResponse::platformSuggestedFilename() const
 {
     return filenameFromHTTPContentDisposition(httpHeaderField(HTTPHeaderName::ContentDisposition));
index f39b958..03dc48c 100644 (file)
@@ -1,3 +1,13 @@
+2018-07-03  Basuke Suzuki  <Basuke.Suzuki@sony.com>
+
+        [Curl] Embed certificate information into ResourceResponse.
+        https://bugs.webkit.org/show_bug.cgi?id=187102
+
+        Reviewed by Youenn Fablet.
+
+        * NetworkProcess/curl/NetworkDataTaskCurl.cpp:
+        (WebKit::NetworkDataTaskCurl::curlDidComplete):.
+
 2018-07-03  Simon Fraser  <simon.fraser@apple.com>
 
         [iOS WK2] We fail to make surfaces volatile when suspending, increasing memory impact
index 50f13f5..fbc89df 100644 (file)
@@ -200,7 +200,7 @@ void NetworkDataTaskCurl::curlDidComplete(CurlRequest& request)
     if (state() == State::Canceling || state() == State::Completed || (!m_client && !isDownload()))
         return;
 
-    m_response.setDeprecatedNetworkLoadMetrics(request.getNetworkLoadMetrics());
+    m_response.setDeprecatedNetworkLoadMetrics(request.networkLoadMetrics().isolatedCopy());
 
     m_client->didCompleteWithError({ }, m_response.deprecatedNetworkLoadMetrics());
 }