You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openoffice.apache.org by da...@apache.org on 2024/04/23 16:46:41 UTC

(openoffice) 02/02: Override OpenSSL's certificate verification with our own, instead of using its verification and selectively overriding the result. - A nonsense self-signed expired certificate is fed into Curl to get it to initialize even when the certificates in its expected system path are missing or elsewhere. - In Curl's CURLOPT_SSL_CTX_FUNCTION, our Curl_SSLContextCallback, we then completely override OpenSSL's verification process with ours, using SSL_CTX_set_cert_verify_callback() (instead o [...]

This is an automated email from the ASF dual-hosted git repository.

damjan pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/openoffice.git

commit f7b97bf7d9139c8b602d3da3aadbeef0631e39c1
Author: Damjan Jovanovic <da...@apache.org>
AuthorDate: Sun Apr 21 17:07:24 2024 +0200

    Override OpenSSL's certificate verification with our own, instead of
    using its verification and selectively overriding the result.
    - A nonsense self-signed expired certificate is fed into Curl to get it
      to initialize even when the certificates in its expected system path
      are missing or elsewhere.
    - In Curl's CURLOPT_SSL_CTX_FUNCTION, our Curl_SSLContextCallback, we
      then completely override OpenSSL's verification process with ours,
      using SSL_CTX_set_cert_verify_callback() (instead of the previous
      SSL_CTX_set_verify() which just allows us to override OpenSSL's
      verification result).
    - The verification is largely the same as before, we just have to call
      slightly different functions to retrieve the certificate to verify and
      the untrusted chain.
    - Create components using the component context, not the legacy multi
      service factory.
    - Various other cleanups, better logging, etc. were made in the process.
    
    Patch by: me
---
 main/ucb/source/ucp/webdav/CurlSession.cxx | 197 ++++++++++++++---------------
 main/ucb/source/ucp/webdav/CurlSession.hxx |  14 +-
 2 files changed, 103 insertions(+), 108 deletions(-)

diff --git a/main/ucb/source/ucp/webdav/CurlSession.cxx b/main/ucb/source/ucp/webdav/CurlSession.cxx
index 73328b78d5..55c654ffc2 100644
--- a/main/ucb/source/ucp/webdav/CurlSession.cxx
+++ b/main/ucb/source/ucp/webdav/CurlSession.cxx
@@ -47,13 +47,10 @@
 #include "webdavprovider.hxx"
 
 
-#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
 #include <com/sun/star/logging/LogLevel.hpp>
 #include <com/sun/star/security/XCertificate.hpp>
 #include <com/sun/star/security/CertificateValidity.hpp>
 #include <com/sun/star/security/CertificateContainerStatus.hpp>
-#include <com/sun/star/security/CertificateContainer.hpp>
-#include <com/sun/star/security/XCertificateContainer.hpp>
 #include <com/sun/star/security/CertAltNameEntry.hpp>
 #include <com/sun/star/security/XSanExtension.hpp>
 #include <com/sun/star/ucb/Lock.hpp>
@@ -130,6 +127,48 @@ CurlSession::CurlSession(
         curl_easy_setopt( m_pCurl, CURLOPT_DEBUGDATA, this );
         curl_easy_setopt( m_pCurl, CURLOPT_VERBOSE, 1L);
     }
+
+    // Create a certificate container.
+    if( !m_aContext.createComponent( "com.sun.star.security.CertificateContainer", m_xCertificateContainer ) )
+        throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Failed to create com.sun.star.security.CertificateContainer" ) );
+    uno::Reference< xml::crypto::XSEInitializer > xSEInitializer;
+    if( !m_aContext.createComponent( "com.sun.star.xml.crypto.SEInitializer", xSEInitializer ) )
+        throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Failed to create com.sun.star.xml.crypto.SEInitializer" ) );
+    m_xSecurityContext = xSEInitializer->createSecurityContext( rtl::OUString() );
+    if( m_xSecurityContext.is() )
+        m_xSecurityEnv = m_xSecurityContext->getSecurityEnvironment();
+    if ( ! m_xSecurityContext.is() || ! m_xSecurityEnv.is())
+        throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Failure creating security services for certificate verification" ) );
+
+    // Populate one nonsense certificate, which we won't ever really use, just to get Curl to initialize:
+    struct curl_blob blob;
+    blob.data = (void*)
+        "-----BEGIN CERTIFICATE-----\n"
+        "MIIC/zCCAeegAwIBAgIUQYFHL3Bv7alQBtXQWy9SXGusm5YwDQYJKoZIhvcNAQEL\n"
+        "BQAwDzENMAsGA1UEAwwEVEVTVDAeFw0yNDA0MjExNzU3MzdaFw0yNDA0MjIxNzU3\n"
+        "MzdaMA8xDTALBgNVBAMMBFRFU1QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n"
+        "AoIBAQCZSXla2TE7GU6xOfie5uilpRf7KQflWcQRgwTCFhk0yzbsSPJYdqbuUqfx\n"
+        "k0pV9Sx8GIkvc7jKQBwS79T15qn6dAZOF40x/k2jEMq150oc/80+dqeNP2jWvxv7\n"
+        "FjgBKSiuGUaHldy6XU3NhrA9G1Ys2/yHQRXER1NTeknEzPiPlobRUk1sNR2Prc5r\n"
+        "0u6cdUWGhbDOKDV9jjvA/14jmaAK+vUqrzzAdiOHVrkglA5oyBKX0BUokRCa8jID\n"
+        "34tH9zeuvozA3xXCi8l9to+HOgT/n7LAGeOSnNPeSHC/xkwumt/rJ05tL9DXg6Ud\n"
+        "3Pjf8KZM+FWJsjoJkcwBR0P2Qh3FAgMBAAGjUzBRMB0GA1UdDgQWBBR7pCl5msAz\n"
+        "rGApirAQ+/tFuHl5kDAfBgNVHSMEGDAWgBR7pCl5msAzrGApirAQ+/tFuHl5kDAP\n"
+        "BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBDJ1S51MKlafDAfFbU\n"
+        "DJcxw3JNHn+VxQuaQQpeeqLIn3rgKHRBV9eOTYHf8AMoCYdQfPs1z45vqBmcyrDw\n"
+        "LoXL6vlUbSLUuYFyfCaFup3bbh2lLozsLcD6bcvV07amX6V3u0ZOKpwqhg+k/IJd\n"
+        "cPVM8jYAnNZZYD6rMHWnW5ZgMFSzSj3Jyyaov/3zwixvFZdViBG+R2RmJZVgMiFP\n"
+        "PNxY3USKiHqdwZIszf3G63Ku0EYtFf3KN8YpoqSMDCDfjL0NhJOtkBUs5HL+4XfK\n"
+        "hToBqJojDMLFRdVIhPQX1LoPd92CUwhueIrYTikScAqY2TIwXpPH0kBjfrVDus8s\n"
+        "vPAk\n"
+        "-----END CERTIFICATE-----";
+    blob.len = strlen( (char*) blob.data ) + 1;
+    blob.flags = CURL_BLOB_COPY;
+    CURLcode rc;
+    rc = curl_easy_setopt( m_pCurl, CURLOPT_CAINFO_BLOB, &blob );
+    if( rc != CURLE_OK )
+        throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii("Error initializing Curl certificate" ) );
+
     m_aLogger.log( LogLevel::INFO, "CurlSession::CurlSession with URL $1$",
         rtl::OUStringToOString( inUri, RTL_TEXTENCODING_UTF8 ).getStr() );
 }
@@ -333,29 +372,17 @@ CURLcode CurlSession::Curl_SSLContextCallback( CURL *, void *ssl_ctx, void *user
     CurlSession *session = static_cast<CurlSession*>( userptr );
     SSL_CTX *context = static_cast<SSL_CTX*>( ssl_ctx );
     SSL_CTX_set_app_data( context, session );
-    SSL_CTX_set_verify( context, SSL_VERIFY_PEER, OPENSSL_ValidateServerCertificate );
+    SSL_CTX_set_cert_verify_callback( context, OPENSSL_VerifyCertificate, session );
     return CURLE_OK;
 }
 
-int CurlSession::OPENSSL_ValidateServerCertificate( int preverify_ok, X509_STORE_CTX *x509_ctx )
+int CurlSession::OPENSSL_VerifyCertificate( X509_STORE_CTX *x509_ctx, void *arg )
 {
-    SSL *ssl = static_cast<SSL*> (
-        X509_STORE_CTX_get_ex_data( x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx() ) );
-    SSL_CTX *ssl_ctx = SSL_get_SSL_CTX( ssl );
-    CurlSession *session = static_cast<CurlSession*>( SSL_CTX_get_app_data( ssl_ctx ) );
-    int verifyOk = session->validateServerX509Certificate( x509_ctx, preverify_ok );
-    // When a certificate's verification fails within OpenSSL, yet passes from the
-    // SSL_CTX_set_verify() callback (ie. this function) (by returning 1),
-    // OpenSSL allows the connection to proceed, yet stores that last verification
-    // error and allows it to be retrieved using SSL_get_verify_result(3).
-    //
-    // Unfortunately, Curl calls SSL_get_verify_result(3) internally, and treats
-    // errors as terminal, disconnecting with an error even when we return 1 here.
-    // Therefore, to approve a certificate that OpenSSL would reject, we have to
-    // both return 1, and overwrite the X509_STORE_CTX's last error with X509_V_OK:
-    if ( verifyOk )
-        X509_STORE_CTX_set_error( x509_ctx, X509_V_OK );
-    return verifyOk;
+    CurlSession *session = static_cast<CurlSession*>( arg );
+    int verifyResult = session->verifyServerX509Certificate( x509_ctx );
+    // We have to both return 1 or 0, and set the X509_V_* error code with X509_STORE_CTX_set_error():
+    X509_STORE_CTX_set_error( x509_ctx, verifyResult );
+    return verifyResult == X509_V_OK ? 1 : 0;
 }
 
 static uno::Sequence< sal_Int8 > convertCertificateToAsn1Der( X509 *certificate )
@@ -373,94 +400,56 @@ static uno::Sequence< sal_Int8 > convertCertificateToAsn1Der( X509 *certificate
         return uno::Sequence< sal_Int8 >();
 }
 
-int CurlSession::validateServerX509Certificate( X509_STORE_CTX *x509StoreContext, int verifyOk )
+int CurlSession::verifyServerX509Certificate( X509_STORE_CTX *x509StoreContext )
 {
-    X509 *serverCertificate = X509_STORE_CTX_get_current_cert( x509StoreContext );
-    int depth = X509_STORE_CTX_get_error_depth( x509StoreContext );
-    STACK_OF(X509) *chain =
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-         X509_STORE_CTX_get0_chain( x509StoreContext );
-#else
-         X509_STORE_CTX_get_chain( x509StoreContext );
-#endif
+    X509 *serverCertificate = X509_STORE_CTX_get0_cert( x509StoreContext );
+    STACK_OF(X509) *chain = X509_STORE_CTX_get0_untrusted( x509StoreContext );
     
     std::vector< uno::Sequence< sal_Int8 > > asn1DerCertificates;
+    int verifyResult = X509_V_OK;
     if ( chain != NULL ) {
         int nCertificates = sk_X509_num( chain );
-        for ( int i = 0; i < nCertificates; i++ ) {
+        for ( int i = 0; i < nCertificates && verifyResult == X509_V_OK; i++ ) {
             X509 *certificate = sk_X509_value( chain, i );
             uno::Sequence< sal_Int8 > asn1DerCertificate = convertCertificateToAsn1Der( certificate );
-            if ( asn1DerCertificate.getLength() == 0 )
-                return 0;
-            asn1DerCertificates.push_back( asn1DerCertificate );
+            if( asn1DerCertificate.getLength() > 0 )
+                asn1DerCertificates.push_back( asn1DerCertificate );
+            else
+                verifyResult = X509_V_ERR_UNSPECIFIED;
         }
     } else {
         uno::Sequence< sal_Int8 > asn1DerCertificate = convertCertificateToAsn1Der( serverCertificate );
-        if ( asn1DerCertificate.getLength() == 0 )
-            return 0;
-        asn1DerCertificates.push_back( asn1DerCertificate );
+        if( asn1DerCertificate.getLength() > 0 )
+            asn1DerCertificates.push_back( asn1DerCertificate );
+        else
+            verifyResult = X509_V_ERR_UNSPECIFIED;
     }
-    verifyOk = verifyCertificateChain( asn1DerCertificates );
+    if( verifyResult == X509_V_OK )
+        verifyResult = verifyCertificateChain( asn1DerCertificates );
 
-    m_aLogger.log( LogLevel::FINE, "validateServerX509Certificate() returning $1$ at depth $2$",
-        (sal_Int32)verifyOk, (sal_Int32)depth );
-    return verifyOk;
+    rtl::OUString verifyErrorString = rtl::OUString::createFromAscii( X509_verify_cert_error_string( verifyResult ) );
+    m_aLogger.log( LogLevel::FINE, "validateServerX509Certificate() verifyResult=$1$ ($2$)",
+        (sal_Int32)verifyResult, verifyErrorString );
+    return verifyResult;
 }
 
 int CurlSession::verifyCertificateChain (
     std::vector< uno::Sequence< sal_Int8 > > &asn1DerCertificates )
 {
     // Check arguments.
-    if (asn1DerCertificates.size()<=0)
+    if( asn1DerCertificates.size() <= 0 )
     {
-        OSL_ASSERT(asn1DerCertificates.size()>0);
-        return 0;
-    }
-
-    // Create some crypto objects to decode and handle the base64
-    // encoded certificate chain.
-    uno::Reference< xml::crypto::XSEInitializer > xSEInitializer;
-    uno::Reference< security::XCertificateContainer > xCertificateContainer;
-    uno::Reference< xml::crypto::XXMLSecurityContext > xSecurityContext;
-    uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnv;
-    try
-    {
-        // Create a certificate container.
-        xCertificateContainer = uno::Reference< security::XCertificateContainer >(
-            getMSF()->createInstance(
-                rtl::OUString::createFromAscii(
-                    "com.sun.star.security.CertificateContainer" ) ),
-            uno::UNO_QUERY_THROW);
-
-        xSEInitializer = uno::Reference< xml::crypto::XSEInitializer >(
-            getMSF()->createInstance(
-                rtl::OUString::createFromAscii( "com.sun.star.xml.crypto.SEInitializer" ) ),
-            uno::UNO_QUERY_THROW);
-
-        xSecurityContext = xSEInitializer->createSecurityContext( rtl::OUString() );
-        if (xSecurityContext.is())
-            xSecurityEnv = xSecurityContext->getSecurityEnvironment();
-
-        if ( ! xSecurityContext.is() || ! xSecurityEnv.is())
-        {
-            // Do we have to dispose xSEInitializer or xCertificateContainer?
-            m_aLogger.log( LogLevel::WARNING, "Failure creating security services for certificate verification" );
-            return 0;
-        }
-    }
-    catch ( uno::Exception const &e)
-    {
-        m_aLogger.log( LogLevel::WARNING, "Error creating security services: $1$", e.Message );
-        return 0;
+        m_aLogger.log( LogLevel::WARNING, "No certificates to verify - failing!" );
+        return X509_V_ERR_UNSPECIFIED;
     }
 
     // Decode the server certificate.
     uno::Reference< security::XCertificate > xServerCertificate(
-        xSecurityEnv->createCertificateFromRaw( asn1DerCertificates[0] ) );
+        m_xSecurityEnv->createCertificateFromRaw( asn1DerCertificates[0] ) );
     if ( ! xServerCertificate.is())
     {
         m_aLogger.log( LogLevel::WARNING, "Failed to create XCertificate" );
-        return 0;
+        return X509_V_ERR_UNSPECIFIED;
     }
 
     // Get the subject from the server certificate.
@@ -485,15 +474,15 @@ int CurlSession::verifyCertificateChain (
     // entry for the server then we do not have to authenticate any
     // certificate.
     const security::CertificateContainerStatus eStatus (
-        xCertificateContainer->hasCertificate(
+        m_xCertificateContainer->hasCertificate(
             getHostName(), sServerCertificateSubject ) );
     if (eStatus != security::CertificateContainerStatus_NOCERT)
     {
         m_aLogger.log( LogLevel::FINER, "Cached certificate found with status=$1$",
                 eStatus == security::CertificateContainerStatus_TRUSTED ? "trusted" : "untrusted" );
         return eStatus == security::CertificateContainerStatus_TRUSTED
-               ? 1
-               : 0;
+               ? X509_V_OK
+               : X509_V_ERR_CERT_UNTRUSTED;
     }
 
     // The shortcut failed, so try to verify the whole chain. This is
@@ -503,15 +492,15 @@ int CurlSession::verifyCertificateChain (
     for (nIndex=0; nIndex < asn1DerCertificates.size(); ++nIndex)
     {
         uno::Reference< security::XCertificate > xCertificate(
-            xSecurityEnv->createCertificateFromRaw( asn1DerCertificates[ nIndex ] ) );
+            m_xSecurityEnv->createCertificateFromRaw( asn1DerCertificates[ nIndex ] ) );
         if ( ! xCertificate.is())
         {
-            m_aLogger.log( LogLevel::FINE, "Failed to create XCertificate $1$", nIndex );
-            return 0;
+            m_aLogger.log( LogLevel::WARNING, "Failed to create XCertificate $1$", nIndex );
+            return X509_V_ERR_UNSPECIFIED;
         }
         aChain.push_back(xCertificate);
     }
-    const sal_Int64 nVerificationResult (xSecurityEnv->verifyCertificate(
+    const sal_Int64 nVerificationResult (m_xSecurityEnv->verifyCertificate(
             xServerCertificate,
             ::comphelper::containerToSequence(aChain)));
 
@@ -557,8 +546,8 @@ int CurlSession::verifyCertificateChain (
         if (nVerificationResult == 0)
         {
             m_aLogger.log( LogLevel::FINE, "Certificate (chain) is valid" );
-            xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, sal_True);
-            return 1;
+            m_xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, sal_True);
+            return X509_V_OK;
         }
         else if ((nVerificationResult & security::CertificateValidity::CHAIN_INCOMPLETE) != 0)
         {
@@ -566,14 +555,14 @@ int CurlSession::verifyCertificateChain (
             // neither automatically (as we just discovered) nor
             // manually (so there is no point in showing any dialog.)
             m_aLogger.log( LogLevel::WARNING, "Certificate (chain) is incomplete" );
-            return 0;
+            return X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
         }
         else if ((nVerificationResult & security::CertificateValidity::REVOKED) != 0)
         {
             // Certificate (chain) is invalid.
             m_aLogger.log( LogLevel::WARNING, "Certificate (chain) is revoked" );
-            xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject,  sal_False);
-            return 0;
+            m_xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject,  sal_False);
+            return X509_V_ERR_CERT_REVOKED;
         }
         else
         {
@@ -604,15 +593,15 @@ int CurlSession::verifyCertificateChain (
                 if ( xApprove.is() )
                 {
                     m_aLogger.log( LogLevel::FINE, "The user approved the certificate" );
-                    xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_True );
-                    return 1;
+                    m_xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_True );
+                    return X509_V_OK;
                 }
                 else
                 {
                     // Don't trust cert
-                    m_aLogger.log( LogLevel::FINE, "The user REJECTED the certificate" );
-                    xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_False );
-                    return 0;
+                    m_aLogger.log( LogLevel::WARNING, "The user REJECTED the certificate" );
+                    m_xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_False );
+                    return X509_V_ERR_CERT_REJECTED;
                 }
             }
         }
@@ -620,13 +609,13 @@ int CurlSession::verifyCertificateChain (
         {
             // Don't trust cert
             m_aLogger.log( LogLevel::WARNING, "Couldn't create the interaction handler for user feedback, rejecting the certificate" );
-            xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_False );
-            return 0;
+            m_xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_False );
+            return X509_V_ERR_CERT_REJECTED;
         }
     }
     m_aLogger.log( LogLevel::WARNING, "No XCommandEnvironment, rejecting the certificate" );
 
-    return 0;
+    return X509_V_ERR_CERT_REJECTED;
 }
 
 bool CurlSession::Curl_ProvideCredentials( long statusCode, void *userdata ) throw (DAVException)
diff --git a/main/ucb/source/ucp/webdav/CurlSession.hxx b/main/ucb/source/ucp/webdav/CurlSession.hxx
index 091b304324..96a03c6f8f 100644
--- a/main/ucb/source/ucp/webdav/CurlSession.hxx
+++ b/main/ucb/source/ucp/webdav/CurlSession.hxx
@@ -37,6 +37,10 @@
 #include "CurlUri.hxx"
 #include "CurlInputStream.hxx"
 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/security/CertificateContainer.hpp>
+#include <com/sun/star/security/XCertificateContainer.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
 #include <curl/curl.h>
 #include <openssl/ssl.h>
 
@@ -75,6 +79,10 @@ private:
 
     static CurlLockStore m_aCurlLockStore;
 
+    ::com::sun::star::uno::Reference< ::com::sun::star::security::XCertificateContainer > m_xCertificateContainer;
+    ::com::sun::star::uno::Reference< ::com::sun::star::xml::crypto::XXMLSecurityContext > m_xSecurityContext;
+    ::com::sun::star::uno::Reference< ::com::sun::star::xml::crypto::XSecurityEnvironment > m_xSecurityEnv;
+
     bool isSSLNeeded();
 
 
@@ -89,10 +97,8 @@ private:
     static CURLcode         Curl_SSLContextCallback( CURL *curl,
                                                      void *ssl_ctx,
                                                      void *userptr );
-    static int              OPENSSL_ValidateServerCertificate( int preverify_ok,
-                                                               X509_STORE_CTX *x509_ctx );
-    int                     validateServerX509Certificate( X509_STORE_CTX *x509StoreContext,
-                                                           int preverifyOk );
+    static int              OPENSSL_VerifyCertificate( X509_STORE_CTX *x509_ctx, void *arg );
+    int                     verifyServerX509Certificate( X509_STORE_CTX *x509StoreContext );
     int                     verifyCertificateChain (
                                 std::vector< uno::Sequence< sal_Int8 > > &asn1DerCertificates );