diff options
Diffstat (limited to 'Swiften/TLS')
31 files changed, 2179 insertions, 243 deletions
diff --git a/Swiften/TLS/CAPICertificate.cpp b/Swiften/TLS/CAPICertificate.cpp index a46b9f6..526b535 100644 --- a/Swiften/TLS/CAPICertificate.cpp +++ b/Swiften/TLS/CAPICertificate.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2012-2016 Isode Limited. + * Copyright (c) 2012-2019 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -14,6 +14,7 @@  #include <Swiften/Base/Log.h>  #include <Swiften/Network/TimerFactory.h>  #include <Swiften/StringCodecs/Hexify.h> +#include <Swiften/TLS/Schannel/SchannelUtil.h>  // Size of the SHA1 hash  #define SHA1_HASH_LEN 20 @@ -21,7 +22,7 @@  #define DEBUG_SCARD_STATUS(function, status) \  { \      std::shared_ptr<boost::system::error_code> errorCode = std::make_shared<boost::system::error_code>(status, boost::system::system_category()); \ -    SWIFT_LOG(debug) << std::hex << function << ": status: 0x" << status << ": " << errorCode->message() << std::endl; \ +    SWIFT_LOG(debug) << std::hex << function << ": status: 0x" << status << ": " << errorCode->message(); \  }  namespace Swift { @@ -43,6 +44,7 @@ CAPICertificate::CAPICertificate(const std::string& capiUri, TimerFactory* timer  }  CAPICertificate::~CAPICertificate() { +    SWIFT_LOG(debug) << "Destroying the CAPICertificate";      if (smartCardTimer_) {          smartCardTimer_->stop();          smartCardTimer_->onTick.disconnect(boost::bind(&CAPICertificate::handleSmartCardTimerTick, this)); @@ -50,7 +52,9 @@ CAPICertificate::~CAPICertificate() {      }      if (certStoreHandle_) { -        CertCloseStore(certStoreHandle_, 0); +        if (CertCloseStore(certStoreHandle_, 0) == FALSE) { +            SWIFT_LOG(debug) << "Failed to close the certificate store handle"; +        }      }      if (cardHandle_) { @@ -80,7 +84,7 @@ const std::string& CAPICertificate::getSmartCardReaderName() const {      return smartCardReaderName_;  } -PCCERT_CONTEXT findCertificateInStore (HCERTSTORE certStoreHandle, const std::string &certName) { +PCCERT_CONTEXT findCertificateInStore(HCERTSTORE certStoreHandle, const std::string &certName) {      if (!boost::iequals(certName.substr(0, 5), "sha1:")) {          // Find client certificate. Note that this sample just searches for a @@ -105,8 +109,7 @@ PCCERT_CONTEXT findCertificateInStore (HCERTSTORE certStoreHandle, const std::st  } - -void CAPICertificate::setUri (const std::string& capiUri) { +void CAPICertificate::setUri(const std::string& capiUri) {      valid_ = false;      /* Syntax: "certstore:" <cert_store> ":" <hash> ":" <hash_of_cert> */ @@ -118,7 +121,7 @@ void CAPICertificate::setUri (const std::string& capiUri) {      /* Substring of subject: uses "storename" */      std::string capiIdentity = capiUri.substr(10);      std::string newCertStoreName; -    size_t pos = capiIdentity.find_first_of (':'); +    size_t pos = capiIdentity.find_first_of(':');      if (pos == std::string::npos) {          /* Using the default certificate store */ @@ -146,49 +149,38 @@ void CAPICertificate::setUri (const std::string& capiUri) {      certStore_ = newCertStoreName; -    PCCERT_CONTEXT certContext = findCertificateInStore (certStoreHandle_, certName_); - +    ScopedCertContext certContext(findCertificateInStore(certStoreHandle_, certName_));      if (!certContext) {          return;      } -      /* Now verify that we can have access to the corresponding private key */      DWORD len; -    CRYPT_KEY_PROV_INFO *pinfo; -    HCRYPTPROV hprov; -    HCRYPTKEY key; -      if (!CertGetCertificateContextProperty(certContext,              CERT_KEY_PROV_INFO_PROP_ID,              NULL,              &len)) { -        CertFreeCertificateContext(certContext); +        SWIFT_LOG(error) << "Error while retrieving context properties";          return;      } -    pinfo = static_cast<CRYPT_KEY_PROV_INFO *>(malloc(len)); +    std::shared_ptr<CRYPT_KEY_PROV_INFO> pinfo(static_cast<CRYPT_KEY_PROV_INFO *>(malloc(len)), free);      if (!pinfo) { -        CertFreeCertificateContext(certContext);          return;      } -    if (!CertGetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, pinfo, &len)) { -        CertFreeCertificateContext(certContext); -        free(pinfo); +    if (!CertGetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, pinfo.get(), &len)) {          return;      } +    certContext.FreeContext(); -    CertFreeCertificateContext(certContext); - +    HCRYPTPROV hprov;      // Now verify if we have access to the private key      if (!CryptAcquireContextW(&hprov, pinfo->pwszContainerName, pinfo->pwszProvName, pinfo->dwProvType, 0)) { -        free(pinfo);          return;      } -      char smartCardReader[1024];      DWORD bufferLength = sizeof(smartCardReader);      if (!CryptGetProvParam(hprov, PP_SMARTCARD_READER, (BYTE *)&smartCardReader, &bufferLength, 0)) { @@ -205,19 +197,19 @@ void CAPICertificate::setUri (const std::string& capiUri) {              smartCardTimer_ = timerFactory_->createTimer(SMARTCARD_EJECTION_CHECK_FREQUENCY_MILLISECONDS);          }          else { -            ///Need to handle an error here +            CryptReleaseContext(hprov, 0); +            return;          }      } +    HCRYPTKEY key;      if (!CryptGetUserKey(hprov, pinfo->dwKeySpec, &key)) {          CryptReleaseContext(hprov, 0); -        free(pinfo);          return;      }      CryptDestroyKey(key);      CryptReleaseContext(hprov, 0); -    free(pinfo);      if (smartCardTimer_) {          smartCardTimer_->onTick.connect(boost::bind(&CAPICertificate::handleSmartCardTimerTick, this)); @@ -227,7 +219,7 @@ void CAPICertificate::setUri (const std::string& capiUri) {      valid_ = true;  } -static void smartcard_check_status (SCARDCONTEXT hContext, +static void smartcard_check_status(SCARDCONTEXT hContext,          const char* pReader,          SCARDHANDLE hCardHandle, /* Can be 0 on the first call */          SCARDHANDLE* newCardHandle, /* The handle returned */ @@ -288,37 +280,35 @@ static void smartcard_check_status (SCARDCONTEXT hContext,      }  } -bool CAPICertificate::checkIfSmartCardPresent () { +bool CAPICertificate::checkIfSmartCardPresent() {      if (!smartCardReaderName_.empty()) {          DWORD dwState;          smartcard_check_status(scardContext_, smartCardReaderName_.c_str(), cardHandle_, &cardHandle_, &dwState);          switch (dwState) {              case SCARD_ABSENT: -                SWIFT_LOG(debug) << "Card absent." << std::endl; +                SWIFT_LOG(debug) << "Card absent.";                  break;              case SCARD_PRESENT: -                SWIFT_LOG(debug) << "Card present." << std::endl; +                SWIFT_LOG(debug) << "Card present.";                  break;              case SCARD_SWALLOWED: -                SWIFT_LOG(debug) << "Card swallowed." << std::endl; +                SWIFT_LOG(debug) << "Card swallowed.";                  break;              case SCARD_POWERED: -                SWIFT_LOG(debug) << "Card has power." << std::endl; +                SWIFT_LOG(debug) << "Card has power.";                  break;              case SCARD_NEGOTIABLE: -                SWIFT_LOG(debug) << "Card reset and waiting PTS negotiation." << std::endl; +                SWIFT_LOG(debug) << "Card reset and waiting PTS negotiation.";                  break;              case SCARD_SPECIFIC: -                SWIFT_LOG(debug) << "Card has specific communication protocols set." << std::endl; +                SWIFT_LOG(debug) << "Card has specific communication protocols set.";                  break;              default: -                SWIFT_LOG(debug) << "Unknown or unexpected card state." << std::endl; +                SWIFT_LOG(debug) << "Unknown or unexpected card state.";                  break;          } - -          switch (dwState) {              case SCARD_ABSENT:                  return false; @@ -342,6 +332,7 @@ bool CAPICertificate::checkIfSmartCardPresent () {  void CAPICertificate::handleSmartCardTimerTick() {      bool poll = checkIfSmartCardPresent();      if (lastPollingResult_ && !poll) { +        SWIFT_LOG(debug) << "CAPI Certificate detected that the certificate card was removed";          onCertificateCardRemoved();      }      lastPollingResult_ = poll; diff --git a/Swiften/TLS/CertificateFactory.cpp b/Swiften/TLS/CertificateFactory.cpp index 487f7cd..d4db3f4 100644 --- a/Swiften/TLS/CertificateFactory.cpp +++ b/Swiften/TLS/CertificateFactory.cpp @@ -1,14 +1,35 @@  /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #include <Swiften/TLS/CertificateFactory.h> +#include <cassert> +#include <memory> +#include <sstream> +#include <string> + +#include <boost/algorithm/string/predicate.hpp> +#include <boost/optional.hpp> + +#include <Swiften/Base/Log.h> +#include <Swiften/StringCodecs/Base64.h> +#include <Swiften/TLS/PrivateKey.h> +  namespace Swift {  CertificateFactory::~CertificateFactory() {  } +std::vector<std::shared_ptr<Certificate>> CertificateFactory::createCertificateChain(const ByteArray& /* data */) { +    assert(false); +    return std::vector<std::shared_ptr<Certificate>>(); +} + +PrivateKey::ref CertificateFactory::createPrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password) { +    return std::make_shared<PrivateKey>(data, password); +} +  } diff --git a/Swiften/TLS/CertificateFactory.h b/Swiften/TLS/CertificateFactory.h index 522a6e6..873c36b 100644 --- a/Swiften/TLS/CertificateFactory.h +++ b/Swiften/TLS/CertificateFactory.h @@ -1,13 +1,17 @@  /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once +#include <boost/optional.hpp> +  #include <Swiften/Base/API.h> +#include <Swiften/Base/SafeByteArray.h>  #include <Swiften/TLS/Certificate.h> +#include <Swiften/TLS/PrivateKey.h>  namespace Swift {      class SWIFTEN_API CertificateFactory { @@ -15,5 +19,7 @@ namespace Swift {              virtual ~CertificateFactory();              virtual Certificate* createCertificateFromDER(const ByteArray& der) = 0; +            virtual std::vector<std::shared_ptr<Certificate>> createCertificateChain(const ByteArray& data); +            PrivateKey::ref createPrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password = boost::optional<SafeByteArray>());      };  } diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp index 17ac8cc..66b650d 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -30,19 +30,27 @@ OpenSSLCertificate::OpenSSLCertificate(const ByteArray& der) {  #else      const unsigned char* p = vecptr(der);  #endif -    cert = std::shared_ptr<X509>(d2i_X509(NULL, &p, der.size()), X509_free); +    cert = std::shared_ptr<X509>(d2i_X509(nullptr, &p, der.size()), X509_free);      if (!cert) { -        SWIFT_LOG(warning) << "Error creating certificate from DER data" << std::endl; +//        SWIFT_LOG(warning) << "Error creating certificate from DER data";      }      parse();  } +void OpenSSLCertificate::incrementReferenceCount() const { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +    X509_up_ref(cert.get()); +#else +    CRYPTO_add(&(cert.get()->references), 1, CRYPTO_LOCK_EVP_PKEY); +#endif +} +  ByteArray OpenSSLCertificate::toDER() const {      ByteArray result;      if (!cert) {          return result;      } -    result.resize(i2d_X509(cert.get(), NULL)); +    result.resize(i2d_X509(cert.get(), nullptr));      unsigned char* p = vecptr(result);      i2d_X509(cert.get(), &p);      return result; diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificate.h b/Swiften/TLS/OpenSSL/OpenSSLCertificate.h index 186caea..64da82a 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLCertificate.h +++ b/Swiften/TLS/OpenSSL/OpenSSLCertificate.h @@ -45,6 +45,8 @@ namespace Swift {                  return cert;              } +            void incrementReferenceCount() const; +          private:              void parse(); diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp new file mode 100644 index 0000000..73058a5 --- /dev/null +++ b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h> + +#include <openssl/pem.h> +#include <openssl/err.h> + +namespace Swift { + +OpenSSLCertificateFactory::OpenSSLCertificateFactory() { +} + +OpenSSLCertificateFactory::~OpenSSLCertificateFactory() { +} + +Certificate* OpenSSLCertificateFactory::createCertificateFromDER(const ByteArray& der) { +    return new OpenSSLCertificate(der); +} + +std::vector<std::shared_ptr<Certificate>> OpenSSLCertificateFactory::createCertificateChain(const ByteArray& data) { +    std::vector<std::shared_ptr<Certificate>> certificateChain; + +    if (data.size() > std::numeric_limits<int>::max()) { +        return certificateChain; +    } + +    auto bio = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free); +    BIO_write(bio.get(), vecptr(data), int(data.size())); + +    // Attempt parsing data as PEM +    X509* openSSLCert = nullptr; +    auto x509certFromPEM = PEM_read_bio_X509(bio.get(), &openSSLCert, nullptr, nullptr); +    if (x509certFromPEM && openSSLCert) { +        std::shared_ptr<X509> x509Cert(openSSLCert, X509_free); +        certificateChain.emplace_back(std::make_shared<OpenSSLCertificate>(x509Cert)); +        openSSLCert = nullptr; +        while ((x509certFromPEM = PEM_read_bio_X509(bio.get(), &openSSLCert, nullptr, nullptr)) != nullptr) { +            std::shared_ptr<X509> x509Cert(openSSLCert, X509_free); +            certificateChain.emplace_back(std::make_shared<OpenSSLCertificate>(x509Cert)); +            openSSLCert = nullptr; +        } +    } + +    // Clear any (expected) errors which resulted from PEM parsing +    // If we don't do this, any existing TLS context will detect these +    // spurious errors and fail to work +    ERR_clear_error(); + +    return certificateChain; +} + +} diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h index c996cd5..a6974c8 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h +++ b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010-2014 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -12,8 +12,10 @@  namespace Swift {      class OpenSSLCertificateFactory : public CertificateFactory {          public: -            virtual Certificate* createCertificateFromDER(const ByteArray& der) { -                return new OpenSSLCertificate(der); -            } +            OpenSSLCertificateFactory(); +            virtual ~OpenSSLCertificateFactory() override final; + +            virtual Certificate* createCertificateFromDER(const ByteArray& der) override final; +            virtual std::vector<std::shared_ptr<Certificate>> createCertificateChain(const ByteArray& data) override final;      };  } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp index 0805917..86b0504 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp @@ -1,8 +1,9 @@  /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ +  #include <Swiften/Base/Platform.h>  #ifdef SWIFTEN_PLATFORM_WINDOWS @@ -10,15 +11,21 @@  #include <wincrypt.h>  #endif +#include <cassert> +#include <memory>  #include <vector> + + +#include <openssl/bio.h>  #include <openssl/err.h>  #include <openssl/pkcs12.h> -#include <memory>  #if defined(SWIFTEN_PLATFORM_MACOSX)  #include <Security/Security.h>  #endif +#include <Swiften/Base/Log.h> +#include <Swiften/Base/Algorithm.h>  #include <Swiften/TLS/OpenSSL/OpenSSLContext.h>  #include <Swiften/TLS/OpenSSL/OpenSSLCertificate.h>  #include <Swiften/TLS/CertificateWithKey.h> @@ -35,14 +42,78 @@ namespace Swift {  static const int MAX_FINISHED_SIZE = 4096;  static const int SSL_READ_BUFFERSIZE = 8192; +#define SSL_DEFAULT_VERIFY_DEPTH 5 + +// Callback function declarations for certificate verification +extern "C" { +    static int certVerifyCallback(X509_STORE_CTX *store_ctx, void*); +    static int verifyCallback(int preverify_ok, X509_STORE_CTX *ctx); +} +  static void freeX509Stack(STACK_OF(X509)* stack) {      sk_X509_free(stack);  } -OpenSSLContext::OpenSSLContext() : state_(Start), context_(0), handle_(0), readBIO_(0), writeBIO_(0) { +namespace { +    class OpenSSLInitializerFinalizer { +        public: +            OpenSSLInitializerFinalizer() { +                SSL_load_error_strings(); +                SSL_library_init(); +                OpenSSL_add_all_algorithms(); + +                // Disable compression +                /* +                STACK_OF(SSL_COMP)* compressionMethods = SSL_COMP_get_compression_methods(); +                sk_SSL_COMP_zero(compressionMethods);*/ +            } + +            ~OpenSSLInitializerFinalizer() { +                EVP_cleanup(); +            } + +            OpenSSLInitializerFinalizer(const OpenSSLInitializerFinalizer &) = delete; +    }; + +    std::unique_ptr<SSL_CTX> createSSL_CTX(OpenSSLContext::Mode mode) { +        std::unique_ptr<SSL_CTX> sslCtx; +        switch (mode) { +            case OpenSSLContext::Mode::Client: +                sslCtx = std::unique_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_client_method())); +                break; +            case OpenSSLContext::Mode::Server: +                sslCtx = std::unique_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_server_method())); +                break; +        } +        return sslCtx; +    } + +    std::string openSSLInternalErrorToString() { +        auto bio = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free); +        ERR_print_errors(bio.get()); +        std::string errorString; +        errorString.resize(BIO_pending(bio.get())); +        BIO_read(bio.get(), (void*)errorString.data(), errorString.size()); +        return errorString; +    } + } + +OpenSSLContext::OpenSSLContext(const TLSOptions& options, Mode mode) : mode_(mode), state_(State::Start) {      ensureLibraryInitialized(); -    context_ = SSL_CTX_new(SSLv23_client_method()); -    SSL_CTX_set_options(context_, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); +    context_ = createSSL_CTX(mode_); +    SSL_CTX_set_options(context_.get(), SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + +    if (mode_ == Mode::Server) { +#if OPENSSL_VERSION_NUMBER < 0x1010 +        // Automatically select highest preference curve used for ECDH temporary keys used during +        // key exchange if possible. +        // Since version 1.1.0, this option is always enabled. +        SSL_CTX_set_ecdh_auto(context_.get(), 1); +#endif + +        SSL_CTX_set_tlsext_servername_arg(context_.get(), this); +        SSL_CTX_set_tlsext_servername_callback(context_.get(), OpenSSLContext::handleServerNameCallback); +    }      // TODO: implement CRL checking      // TODO: download CRL (HTTP transport) @@ -50,111 +121,420 @@ OpenSSLContext::OpenSSLContext() : state_(Start), context_(0), handle_(0), readB      // TODO: implement OCSP support      // TODO: handle OCSP stapling see https://www.rfc-editor.org/rfc/rfc4366.txt -    // Load system certs + +    // Default for ignoreSystemTrustAnchors is false, i.e. load System TAs by default, +    // to preserve previous behaviour +    if (!options.ignoreSystemTrustAnchors) { +        // Load system certs  #if defined(SWIFTEN_PLATFORM_WINDOWS) -    X509_STORE* store = SSL_CTX_get_cert_store(context_); -    HCERTSTORE systemStore = CertOpenSystemStore(0, "ROOT"); -    if (systemStore) { -        PCCERT_CONTEXT certContext = NULL; -        while (true) { -            certContext = CertFindCertificateInStore(systemStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, certContext); -            if (!certContext) { -                break; -            } -            OpenSSLCertificate cert(createByteArray(certContext->pbCertEncoded, certContext->cbCertEncoded)); -            if (store && cert.getInternalX509()) { -                X509_STORE_add_cert(store, cert.getInternalX509().get()); +        X509_STORE* store = SSL_CTX_get_cert_store(context_.get()); +        HCERTSTORE systemStore = CertOpenSystemStore(0, "ROOT"); +        if (systemStore) { +            PCCERT_CONTEXT certContext = nullptr; +            while (true) { +                certContext = CertFindCertificateInStore(systemStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, certContext); +                if (!certContext) { +                    break; +                } +                OpenSSLCertificate cert(createByteArray(certContext->pbCertEncoded, certContext->cbCertEncoded)); +                if (store && cert.getInternalX509()) { +                    X509_STORE_add_cert(store, cert.getInternalX509().get()); +                }              }          } -    }  #elif !defined(SWIFTEN_PLATFORM_MACOSX) -    SSL_CTX_set_default_verify_paths(context_); +        SSL_CTX_set_default_verify_paths(context_.get());  #elif defined(SWIFTEN_PLATFORM_MACOSX) && !defined(SWIFTEN_PLATFORM_IPHONE) -    // On Mac OS X 10.5 (OpenSSL < 0.9.8), OpenSSL does not automatically look in the system store. -    // On Mac OS X 10.6 (OpenSSL >= 0.9.8), OpenSSL *does* look in the system store to determine trust. -    // However, if there is a certificate error, it will always emit the "Invalid CA" error if we didn't add -    // the certificates first. See -    //        http://opensource.apple.com/source/OpenSSL098/OpenSSL098-27/src/crypto/x509/x509_vfy_apple.c -    // to understand why. We therefore add all certs from the system store ourselves. -    X509_STORE* store = SSL_CTX_get_cert_store(context_); -    CFArrayRef anchorCertificates; -    if (SecTrustCopyAnchorCertificates(&anchorCertificates) == 0) { -        for (int i = 0; i < CFArrayGetCount(anchorCertificates); ++i) { -            SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(const_cast<void*>(CFArrayGetValueAtIndex(anchorCertificates, i))); -            CSSM_DATA certCSSMData; -            if (SecCertificateGetData(cert, &certCSSMData) != 0 || certCSSMData.Length == 0) { -                continue; -            } -            std::vector<unsigned char> certData; -            certData.resize(certCSSMData.Length); -            memcpy(&certData[0], certCSSMData.Data, certCSSMData.Length); -            OpenSSLCertificate certificate(certData); -            if (store && certificate.getInternalX509()) { -                X509_STORE_add_cert(store, certificate.getInternalX509().get()); +        // On Mac OS X 10.5 (OpenSSL < 0.9.8), OpenSSL does not automatically look in the system store. +        // On Mac OS X 10.6 (OpenSSL >= 0.9.8), OpenSSL *does* look in the system store to determine trust. +        // However, if there is a certificate error, it will always emit the "Invalid CA" error if we didn't add +        // the certificates first. See +        //        http://opensource.apple.com/source/OpenSSL098/OpenSSL098-27/src/crypto/x509/x509_vfy_apple.c +        // to understand why. We therefore add all certs from the system store ourselves. +        X509_STORE* store = SSL_CTX_get_cert_store(context_.get()); +        CFArrayRef anchorCertificates; +        if (SecTrustCopyAnchorCertificates(&anchorCertificates) == 0) { +            for (int i = 0; i < CFArrayGetCount(anchorCertificates); ++i) { +                SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(const_cast<void*>(CFArrayGetValueAtIndex(anchorCertificates, i))); +                CSSM_DATA certCSSMData; +                if (SecCertificateGetData(cert, &certCSSMData) != 0 || certCSSMData.Length == 0) { +                    continue; +                } +                std::vector<unsigned char> certData; +                certData.resize(certCSSMData.Length); +                memcpy(&certData[0], certCSSMData.Data, certCSSMData.Length); +                OpenSSLCertificate certificate(certData); +                if (store && certificate.getInternalX509()) { +                    X509_STORE_add_cert(store, certificate.getInternalX509().get()); +                }              } +            CFRelease(anchorCertificates);          } -        CFRelease(anchorCertificates); -    }  #endif +    } +    configure(options);  }  OpenSSLContext::~OpenSSLContext() { -    SSL_free(handle_); -    SSL_CTX_free(context_);  }  void OpenSSLContext::ensureLibraryInitialized() { -    static bool isLibraryInitialized = false; -    if (!isLibraryInitialized) { -        SSL_load_error_strings(); -        SSL_library_init(); -        OpenSSL_add_all_algorithms(); +    static OpenSSLInitializerFinalizer openSSLInit; +} + +void OpenSSLContext::initAndSetBIOs() { +    // Ownership of BIOs is transferred +    readBIO_ = BIO_new(BIO_s_mem()); +    writeBIO_ = BIO_new(BIO_s_mem()); +    SSL_set_bio(handle_.get(), readBIO_, writeBIO_); +} + +// This callback is called by OpenSSL when a client certificate needs to be verified. +// In turn, this calls the verification callback which the user +// of this OpenSSLContext has configured (if any). +static int certVerifyCallback(X509_STORE_CTX* store_ctx, void* arg) +{ +    OpenSSLContext* context = static_cast<OpenSSLContext *>(arg); + +    // Need to stash store_ctx pointer for use within verification +    context->setX509StoreContext(store_ctx); + +    int ret; + +    // This callback shouldn't have been set up if the context doesn't +    // have a verifyCertCallback set, but it doesn't hurt to double check +    std::function<int (const TLSContext *)> cb = context->getVerifyCertCallback(); +    if (cb != nullptr) { +        ret = cb(static_cast<const OpenSSLContext*>(context)); +    } else { +        SWIFT_LOG(debug) << "certVerifyCallback called but context.verifyCertCallback is unset"; +        ret = 0; +    } + +    context->setX509StoreContext(nullptr); +    return ret; +} + +// Convenience function to generate a text representation +// of an X509 Name. This information is only used for logging. +static std::string X509_NAME_to_text(X509_NAME* name) +{ +    std::string nameString; + +    if (!name) { +        return nameString; +    } + +    std::unique_ptr<BIO, decltype(&BIO_free)> io(BIO_new(BIO_s_mem()), &BIO_free); +    int r = X509_NAME_print_ex(io.get(), name, 0, XN_FLAG_RFC2253); +    BIO_write(io.get(), "\0", 1); + +    if (r > 0) { +        BUF_MEM* ptr = nullptr; +        BIO_get_mem_ptr(io.get(), &ptr); +        nameString = ptr->data; +    } + +    return nameString; +} + +// Check depth of certificate chain +static int verifyCallback(int preverifyOk, X509_STORE_CTX* ctx) +{ +    // Retrieve the pointer to the SSL of the connection currently treated +    // and the application specific data stored into the SSL object. + +    int err = X509_STORE_CTX_get_error(ctx); +    int depth = X509_STORE_CTX_get_error_depth(ctx); -        // Disable compression -        /* -        STACK_OF(SSL_COMP)* compressionMethods = SSL_COMP_get_compression_methods(); -        sk_SSL_COMP_zero(compressionMethods);*/ +    SSL* ssl = static_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); +    SSL_CTX* sslctx = ssl ? SSL_get_SSL_CTX(ssl) : nullptr; +    if (!sslctx) { +        SWIFT_LOG(debug) << "verifyCallback: internal error"; +        return preverifyOk; +    } + +    if (SSL_CTX_get_verify_mode(sslctx) == SSL_VERIFY_NONE) { +        SWIFT_LOG(debug) << "verifyCallback: no verification required"; +        // No verification requested +        return 1; +    } -        isLibraryInitialized = true; +    X509* errCert = X509_STORE_CTX_get_current_cert(ctx); +    std::string subjectString; +    if (errCert) { +        X509_NAME* subjectName = X509_get_subject_name(errCert); +        subjectString = X509_NAME_to_text(subjectName);      } + +    // Catch a too long certificate chain. The depth limit set using +    // SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so +    // that whenever the "depth>verify_depth" condition is met, we +    // have violated the limit and want to log this error condition. +    // We must do it here, because the CHAIN_TOO_LONG error would not +    // be found explicitly; only errors introduced by cutting off the +    // additional certificates would be logged. +    if (depth >= SSL_CTX_get_verify_depth(sslctx)) { +        preverifyOk = 0; +        err = X509_V_ERR_CERT_CHAIN_TOO_LONG; +        X509_STORE_CTX_set_error(ctx, err); +    } + +    if (!preverifyOk) { +        std::string issuerString; +        if (errCert) { +            X509_NAME* issuerName = X509_get_issuer_name(errCert); +            issuerString = X509_NAME_to_text(issuerName); +        } +        SWIFT_LOG(debug) << "verifyCallback: verification error " << +          X509_verify_cert_error_string(err) << " depth: " << +          depth << " issuer: " << ((issuerString.length() > 0) ? issuerString : "<unknown>"); +     } else { +        SWIFT_LOG(debug) << "verifyCallback: SSL depth: " << depth << " Subject: " << +          ((subjectString.length() > 0) ? subjectString : "<>"); +    } +    // Always return "OK", as check on verification status +    // will be performed once TLS handshake has completed, +    // by calling OpenSSLContext::getVerificationErrorTypeForResult() to +    // get the value set via X509_STORE_CTX_set_error() above. +    return 1; +} + +bool OpenSSLContext::configure(const TLSOptions &options) +{ +    if (options.cipherSuites) { +        std::string cipherSuites = *(options.cipherSuites); +        if (SSL_CTX_set_cipher_list(context_.get(), cipherSuites.c_str()) != 1 ) { +            SWIFT_LOG(debug) << "Failed to set cipher-suites"; +            return false; +        } +    } + +    if (options.context) { +        const auto& contextId = *options.context; + +        if (SSL_CTX_set_session_id_context(context_.get(), +                                           reinterpret_cast<const unsigned char *>(contextId.c_str()), +                                           contextId.length()) != 1) { +            SWIFT_LOG(debug) << "Failed to set context-id"; +            return false; +        } +    } + +    if (options.sessionCacheTimeout) { +        int scto = *options.sessionCacheTimeout; +        if (scto <= 0) { +            SWIFT_LOG(debug) << "Invalid value for session-cache-timeout"; +            return false; +        } +        (void)SSL_CTX_set_timeout(context_.get(), scto); +        if (SSL_CTX_get_timeout(context_.get()) != scto) { +            SWIFT_LOG(debug) << "Failed to set session-cache-timeout"; +            return false; +        } +    } + +    if (options.verifyCertificateCallback) { +        verifyCertCallback = *options.verifyCertificateCallback; +    } else { +        verifyCertCallback = nullptr; +    } + +    if (options.verifyMode) { +        TLSOptions::VerifyMode verify_mode = *options.verifyMode; +        int mode; +        switch (verify_mode) { +        case TLSOptions::VerifyMode::None: +            mode = SSL_VERIFY_NONE; +            break; +        case TLSOptions::VerifyMode::Required: +            mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE; +            break; +        case TLSOptions::VerifyMode::Optional: +            mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; +            break; +        } + +        // Set up default certificate chain verification depth - may be overridden below +        SSL_CTX_set_verify_depth(context_.get(), SSL_DEFAULT_VERIFY_DEPTH + 1); + +        // Set callbacks up +        SSL_CTX_set_verify(context_.get(), mode, verifyCallback); + +        // Only set up certificate verification callback if a user callback has +        // been configured via the TLSOptions +        if (verifyCertCallback != nullptr) { +            SSL_CTX_set_cert_verify_callback(context_.get(), certVerifyCallback, this); +        } +    } + +    if (options.verifyDepth) { +        int depth = *options.verifyDepth; +        if (depth <= 0) { +            SWIFT_LOG(debug) << "Invalid value for verify-depth"; +            return false; +        } + +        // Increase depth limit by one, so that verifyCallback() will log it +        SSL_CTX_set_verify_depth(context_.get(), depth + 1); +    } + +    auto updateOptionIfPresent = [this](boost::optional<bool> option, int flag) { +        if (option) { +            if (*option) { +                SSL_CTX_set_options(context_.get(), flag); +            } +            else { +                SSL_CTX_clear_options(context_.get(), flag); +            } +        } +    }; +    updateOptionIfPresent(options.workaroundMicrosoftSessID, SSL_OP_MICROSOFT_SESS_ID_BUG); +    updateOptionIfPresent(options.workaroundNetscapeChallenge, SSL_OP_NETSCAPE_CHALLENGE_BUG); +    updateOptionIfPresent(options.workaroundNetscapeReuseCipherChange, SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG); +    updateOptionIfPresent(options.workaroundSSLRef2ReuseCertType, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG); +    updateOptionIfPresent(options.workaroundMicrosoftBigSSLv3Buffer, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); +    updateOptionIfPresent(options.workaroundSSLeay080ClientDH, SSL_OP_SSLEAY_080_CLIENT_DH_BUG); +    updateOptionIfPresent(options.workaroundTLSD5, SSL_OP_TLS_D5_BUG); +    updateOptionIfPresent(options.workaroundTLSBlockPadding, SSL_OP_TLS_BLOCK_PADDING_BUG); +    updateOptionIfPresent(options.workaroundDontInsertEmptyFragments, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); +    updateOptionIfPresent(options.workaroundAll, SSL_OP_ALL); +    updateOptionIfPresent(options.suppressSSLv2, SSL_OP_NO_SSLv2); +    updateOptionIfPresent(options.suppressSSLv3, SSL_OP_NO_SSLv3); +    updateOptionIfPresent(options.suppressTLSv1, SSL_OP_NO_TLSv1); +    updateOptionIfPresent(options.disableTLSRollBackBug, SSL_OP_TLS_ROLLBACK_BUG); +    updateOptionIfPresent(options.singleDHUse, SSL_OP_SINGLE_DH_USE); + +    if (options.trustAnchors) { +        // Add any additional Trust Anchors which are present in the TLSOptions +        X509_STORE* store = SSL_CTX_get_cert_store(context_.get()); + +        if (store) { +            for (auto& certificate : *options.trustAnchors) { +                auto openSSLCert = dynamic_cast<OpenSSLCertificate*>(certificate.get()); +                if (openSSLCert && openSSLCert->getInternalX509()) { +                    X509_STORE_add_cert(store, openSSLCert->getInternalX509().get()); +                    // Don't need to increment reference count as X509_STORE_add_cert does thiS +                } +            } +        } +    } + +    return true; +} + + +void OpenSSLContext::accept() { +    assert(mode_ == Mode::Server); +    handle_ = std::unique_ptr<SSL>(SSL_new(context_.get())); +    if (!handle_) { +        state_ = State::Error; +        onError(std::make_shared<TLSError>(TLSError::AcceptFailed, openSSLInternalErrorToString())); +        return; +    } + +    initAndSetBIOs(); + +    state_ = State::Accepting; +    doAccept();  }  void OpenSSLContext::connect() { -    handle_ = SSL_new(context_); -    if (handle_ == nullptr) { -        state_ = Error; -        onError(std::make_shared<TLSError>()); +    connect(std::string()); +} + +void OpenSSLContext::connect(const std::string& requestedServerName) { +    assert(mode_ == Mode::Client); +    handle_ = std::unique_ptr<SSL>(SSL_new(context_.get())); +    if (!handle_) { +        state_ = State::Error; +        onError(std::make_shared<TLSError>(TLSError::ConnectFailed, openSSLInternalErrorToString()));          return;      } -    // Ownership of BIOs is ransferred -    readBIO_ = BIO_new(BIO_s_mem()); -    writeBIO_ = BIO_new(BIO_s_mem()); -    SSL_set_bio(handle_, readBIO_, writeBIO_); +    if (!requestedServerName.empty()) { +        if (SSL_set_tlsext_host_name(handle_.get(), const_cast<char*>(requestedServerName.c_str())) != 1) { +            onError(std::make_shared<TLSError>(TLSError::ConnectFailed, "Failed to set Server Name Indication: " + openSSLInternalErrorToString()));\ +            return; +        } +    } + +    // Ownership of BIOs is transferred to the SSL_CTX instance in handle_. +    initAndSetBIOs(); -    state_ = Connecting; +    state_ = State::Connecting;      doConnect();  } +void OpenSSLContext::doAccept() { +    auto acceptResult = SSL_accept(handle_.get()); +    auto error = SSL_get_error(handle_.get(), acceptResult); +    switch (error) { +        case SSL_ERROR_NONE: { +            state_ = State::Connected; +            //std::cout << x->name << std::endl; +            //const char* comp = SSL_get_current_compression(handle_.get()); +            //std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl; +            onConnected(); +            // The following call is important so the client knowns the handshake is finished. +            sendPendingDataToNetwork(); +            sendPendingDataToApplication(); +            break; +        } +        case SSL_ERROR_WANT_READ: +            sendPendingDataToNetwork(); +            break; +        case SSL_ERROR_WANT_WRITE: +            sendPendingDataToNetwork(); +            break; +        default: +            state_ = State::Error; +            onError(std::make_shared<TLSError>(TLSError::AcceptFailed, openSSLInternalErrorToString())); +            sendPendingDataToNetwork(); +    } +} +  void OpenSSLContext::doConnect() { -    int connectResult = SSL_connect(handle_); -    int error = SSL_get_error(handle_, connectResult); +    int connectResult = SSL_connect(handle_.get()); +    int error = SSL_get_error(handle_.get(), connectResult);      switch (error) {          case SSL_ERROR_NONE: { -            state_ = Connected; +            state_ = State::Connected;              //std::cout << x->name << std::endl; -            //const char* comp = SSL_get_current_compression(handle_); +            //const char* comp = SSL_get_current_compression(handle_.get());              //std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl;              onConnected(); +            // The following is needed since OpenSSL 1.1.1 for the server to be able to calculate the +            // TLS finish message. +            sendPendingDataToNetwork();              break;          }          case SSL_ERROR_WANT_READ:              sendPendingDataToNetwork();              break;          default: -            state_ = Error; +            state_ = State::Error;              onError(std::make_shared<TLSError>()); +            onError(std::make_shared<TLSError>(TLSError::ConnectFailed, openSSLInternalErrorToString())); +    } +} + +int OpenSSLContext::handleServerNameCallback(SSL* ssl, int*, void* arg) { +    if (ssl == nullptr) +        return SSL_TLSEXT_ERR_NOACK; + +    const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); +    if (servername) { +        auto serverNameString = std::string(servername); +        auto context = reinterpret_cast<OpenSSLContext*>(arg); +        context->onServerNameRequested(serverNameString); + +        if (context->abortTLSHandshake_) { +            context->abortTLSHandshake_ = false; +            return SSL_TLSEXT_ERR_ALERT_FATAL; +        }      } +    return SSL_TLSEXT_ERR_OK;  }  void OpenSSLContext::sendPendingDataToNetwork() { @@ -170,43 +550,148 @@ void OpenSSLContext::sendPendingDataToNetwork() {  void OpenSSLContext::handleDataFromNetwork(const SafeByteArray& data) {      BIO_write(readBIO_, vecptr(data), data.size());      switch (state_) { -        case Connecting: +        case State::Accepting: +            doAccept(); +            break; +        case State::Connecting:              doConnect();              break; -        case Connected: +        case State::Connected:              sendPendingDataToApplication();              break; -        case Start: assert(false); break; -        case Error: /*assert(false);*/ break; +        case State::Start: assert(false); break; +        case State::Error: /*assert(false);*/ break;      }  }  void OpenSSLContext::handleDataFromApplication(const SafeByteArray& data) { -    if (SSL_write(handle_, vecptr(data), data.size()) >= 0) { -        sendPendingDataToNetwork(); +    auto ret = SSL_write(handle_.get(), vecptr(data), data.size()); +    if (ret > 0 || SSL_get_error(handle_.get(), ret) == SSL_ERROR_WANT_READ) { +            sendPendingDataToNetwork();      }      else { -        state_ = Error; -        onError(std::make_shared<TLSError>()); +        state_ = State::Error; +        onError(std::make_shared<TLSError>(TLSError::UnknownError, openSSLInternalErrorToString()));      }  }  void OpenSSLContext::sendPendingDataToApplication() {      SafeByteArray data;      data.resize(SSL_READ_BUFFERSIZE); -    int ret = SSL_read(handle_, vecptr(data), data.size()); +    int ret = SSL_read(handle_.get(), vecptr(data), data.size());      while (ret > 0) {          data.resize(ret);          onDataForApplication(data);          data.resize(SSL_READ_BUFFERSIZE); -        ret = SSL_read(handle_, vecptr(data), data.size()); +        ret = SSL_read(handle_.get(), vecptr(data), data.size());      } -    if (ret < 0 && SSL_get_error(handle_, ret) != SSL_ERROR_WANT_READ) { -        state_ = Error; -        onError(std::make_shared<TLSError>()); +    if (ret < 0 && SSL_get_error(handle_.get(), ret) != SSL_ERROR_WANT_READ) { +        state_ = State::Error; +        onError(std::make_shared<TLSError>(TLSError::UnknownError, openSSLInternalErrorToString()));      }  } +bool OpenSSLContext::setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& certificateChain) { +    if (certificateChain.size() == 0) { +        SWIFT_LOG(debug) << "Trying to load empty certificate chain."; +        return false; +    } + +    // load endpoint certificate +    auto openSSLCert = dynamic_cast<OpenSSLCertificate*>(certificateChain[0].get()); +    if (!openSSLCert) { +        return false; +    } + +    // This increments the reference count on the X509 certificate automatically +    if (SSL_CTX_use_certificate(context_.get(), openSSLCert->getInternalX509().get()) != 1) { +        return false; +    } + +    if (certificateChain.size() > 1) { +        for (auto certificate = certificateChain.begin() + 1; certificate != certificateChain.end(); ++certificate) { +            auto openSSLCert = dynamic_cast<OpenSSLCertificate*>(certificate->get()); +            if (!openSSLCert) { +                return false; +            } + +            if (SSL_CTX_add_extra_chain_cert(context_.get(), openSSLCert->getInternalX509().get()) != 1) { +                SWIFT_LOG(debug) << "Trying to load empty certificate chain."; +                return false; +            } +            // Have to manually increment reference count as SSL_CTX_add_extra_chain_cert does not do so +            openSSLCert->incrementReferenceCount(); +        } +    } + +    if (handle_) { +        // This workaround is needed as OpenSSL has a shortcut to not do anything +        // if you set the SSL_CTX to the existing SSL_CTX and not reloading the +        // certificates from the SSL_CTX. +        auto dummyContext = createSSL_CTX(mode_); +        SSL_set_SSL_CTX(handle_.get(), dummyContext.get()); +        SSL_set_SSL_CTX(handle_.get(), context_.get()); +    } + +    return true; +} + +int empty_or_preset_password_cb(char* buf, int max_len, int flag, void* password); + +int empty_or_preset_password_cb(char* buf, int max_len, int /* flag */, void* password) { +    char* charPassword = (char*)password; +    if (charPassword == nullptr) { +        return 0; +    } +    int len = strlen(charPassword); +    if(len > max_len) { +        return 0; +    } +    memcpy(buf, charPassword, len); +    return len; +} + +bool OpenSSLContext::setPrivateKey(const PrivateKey::ref& privateKey) { +    if (privateKey->getData().size() > std::numeric_limits<int>::max()) { +        return false; +    } + +    auto bio = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free); +    BIO_write(bio.get(), vecptr(privateKey->getData()), int(privateKey->getData().size())); + +    SafeByteArray safePassword; +    void* password = nullptr; +    if (privateKey->getPassword()) { +        safePassword = privateKey->getPassword().get(); +        safePassword.push_back(0); +        password = safePassword.data(); +    } +    // Make sure resultKey is tidied up by wrapping it in a shared_ptr +    auto resultKey = std::shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio.get(), nullptr, empty_or_preset_password_cb, password), EVP_PKEY_free); +    if (resultKey) { +        if (handle_) { +            auto result = SSL_use_PrivateKey(handle_.get(), resultKey.get()); +            if (result != 1) { +                return false; +            } +        } +        else { +            auto result = SSL_CTX_use_PrivateKey(context_.get(), resultKey.get()); +            if (result != 1) { +                return false; +            } +        } +    } +    else { +        return false; +    } +    return true; +} + +void OpenSSLContext::setAbortTLSHandshake(bool abort) { +    abortTLSHandshake_ = abort; +} +  bool OpenSSLContext::setClientCertificate(CertificateWithKey::ref certificate) {      std::shared_ptr<PKCS12Certificate> pkcs12Certificate = std::dynamic_pointer_cast<PKCS12Certificate>(certificate);      if (!pkcs12Certificate || pkcs12Certificate->isNull()) { @@ -216,16 +701,16 @@ bool OpenSSLContext::setClientCertificate(CertificateWithKey::ref certificate) {      // Create a PKCS12 structure      BIO* bio = BIO_new(BIO_s_mem());      BIO_write(bio, vecptr(pkcs12Certificate->getData()), pkcs12Certificate->getData().size()); -    std::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, NULL), PKCS12_free); +    std::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, nullptr), PKCS12_free);      BIO_free(bio);      if (!pkcs12) {          return false;      }      // Parse PKCS12 -    X509 *certPtr = 0; -    EVP_PKEY* privateKeyPtr = 0; -    STACK_OF(X509)* caCertsPtr = 0; +    X509 *certPtr = nullptr; +    EVP_PKEY* privateKeyPtr = nullptr; +    STACK_OF(X509)* caCertsPtr = nullptr;      SafeByteArray password(pkcs12Certificate->getPassword());      password.push_back(0);      int result = PKCS12_parse(pkcs12.get(), reinterpret_cast<const char*>(vecptr(password)), &privateKeyPtr, &certPtr, &caCertsPtr); @@ -237,32 +722,71 @@ bool OpenSSLContext::setClientCertificate(CertificateWithKey::ref certificate) {      std::shared_ptr<STACK_OF(X509)> caCerts(caCertsPtr, freeX509Stack);      // Use the key & certificates -    if (SSL_CTX_use_certificate(context_, cert.get()) != 1) { +    if (SSL_CTX_use_certificate(context_.get(), cert.get()) != 1) {          return false;      } -    if (SSL_CTX_use_PrivateKey(context_, privateKey.get()) != 1) { +    if (SSL_CTX_use_PrivateKey(context_.get(), privateKey.get()) != 1) {          return false;      }      for (int i = 0;  i < sk_X509_num(caCerts.get()); ++i) { -        SSL_CTX_add_extra_chain_cert(context_, sk_X509_value(caCerts.get(), i)); +        SSL_CTX_add_extra_chain_cert(context_.get(), sk_X509_value(caCerts.get(), i));      }      return true;  } +bool OpenSSLContext::setDiffieHellmanParameters(const ByteArray& parametersInOpenSslDer) { +    auto bio = std::unique_ptr<BIO, decltype(&BIO_free)>(BIO_new(BIO_s_mem()), BIO_free); +    if (bio) { +        BIO_write(bio.get(), vecptr(parametersInOpenSslDer), parametersInOpenSslDer.size()); +        auto result = 0L; +        if (auto dhparams = d2i_DHparams_bio(bio.get(), nullptr)) { +            if (handle_) { +                result = SSL_set_tmp_dh(handle_.get(), dhparams); +            } +            else { +                result = SSL_CTX_set_tmp_dh(context_.get(), dhparams); +            } +            DH_free(dhparams); +        } +        return result == 1; +    } +    return false; +} +  std::vector<Certificate::ref> OpenSSLContext::getPeerCertificateChain() const {      std::vector<Certificate::ref> result; -    STACK_OF(X509)* chain = SSL_get_peer_cert_chain(handle_); + +    // When this context is a server, the peer (client) certificate +    // is obtained via SSL_get_peer_certificate, and any other +    // certificates set by the peer are available via SSL_get_peer_cert_chain. +    // When this context is a client, all of the server's certificates are +    // obtained using SSL_get_peer_cert_chain +    if (mode_ == Mode::Server) { +        auto cert = SSL_get_peer_certificate(handle_.get()); +        if (cert) { +            // Do not need to copy the returned cert as SSL_get_peer_certificate +            // increments the reference count on the certificate +            std::shared_ptr<X509> x509Cert(cert, X509_free); +            Certificate::ref cert = std::make_shared<OpenSSLCertificate>(x509Cert); +            result.push_back(cert); +        } +    } + +    STACK_OF(X509)* chain = SSL_get_peer_cert_chain(handle_.get());      for (int i = 0; i < sk_X509_num(chain); ++i) { +        // Here we do need to copy the returned cert, since SSL_get_peer_cert_chain +        // does not increment the reference count on each certificate          std::shared_ptr<X509> x509Cert(X509_dup(sk_X509_value(chain, i)), X509_free);          Certificate::ref cert = std::make_shared<OpenSSLCertificate>(x509Cert);          result.push_back(cert);      } +      return result;  }  std::shared_ptr<CertificateVerificationError> OpenSSLContext::getPeerCertificateVerificationError() const { -    int verifyResult = SSL_get_verify_result(handle_); +    int verifyResult = SSL_get_verify_result(handle_.get());      if (verifyResult != X509_V_OK) {          return std::make_shared<CertificateVerificationError>(getVerificationErrorTypeForResult(verifyResult));      } @@ -274,11 +798,19 @@ std::shared_ptr<CertificateVerificationError> OpenSSLContext::getPeerCertificate  ByteArray OpenSSLContext::getFinishMessage() const {      ByteArray data;      data.resize(MAX_FINISHED_SIZE); -    size_t size = SSL_get_finished(handle_, vecptr(data), data.size()); +    auto size = SSL_get_finished(handle_.get(), vecptr(data), data.size());      data.resize(size);      return data;  } +ByteArray OpenSSLContext::getPeerFinishMessage() const { +    ByteArray data; +    data.resize(MAX_FINISHED_SIZE); +    auto size = SSL_get_peer_finished(handle_.get(), vecptr(data), data.size()); +    data.resize(size); +    return data; + } +  CertificateVerificationError::Type OpenSSLContext::getVerificationErrorTypeForResult(int result) {      assert(result != 0);      switch (result) { diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h index e75b3c9..8eb5758 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.h +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h @@ -1,11 +1,13 @@  /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once +#include <memory> +  #include <boost/noncopyable.hpp>  #include <boost/signals2.hpp> @@ -14,41 +16,77 @@  #include <Swiften/Base/ByteArray.h>  #include <Swiften/TLS/CertificateWithKey.h>  #include <Swiften/TLS/TLSContext.h> +#include <Swiften/TLS/TLSOptions.h> -namespace Swift { +namespace std { +    template<> +    class default_delete<SSL_CTX> { +    public: +        void operator()(SSL_CTX *ptr) { +            SSL_CTX_free(ptr); +        } +    }; + +    template<> +    class default_delete<SSL> { +    public: +        void operator()(SSL *ptr) { +            SSL_free(ptr); +        } +    }; +} +namespace Swift {      class OpenSSLContext : public TLSContext, boost::noncopyable {          public: -            OpenSSLContext(); -            virtual ~OpenSSLContext(); +            OpenSSLContext(const TLSOptions& options, Mode mode); +            virtual ~OpenSSLContext() override final; + +            void accept() override final; +            void connect() override final; +            void connect(const std::string& requestHostname) override final; + +            bool setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& certificateChain) override final; +            bool setPrivateKey(const PrivateKey::ref& privateKey) override final; +            bool setClientCertificate(CertificateWithKey::ref cert) override final; +            void setAbortTLSHandshake(bool abort) override final; +            bool setDiffieHellmanParameters(const ByteArray& parametersInOpenSslDer) override final; -            void connect(); -            bool setClientCertificate(CertificateWithKey::ref cert); +            void handleDataFromNetwork(const SafeByteArray&) override final; +            void handleDataFromApplication(const SafeByteArray&) override final; -            void handleDataFromNetwork(const SafeByteArray&); -            void handleDataFromApplication(const SafeByteArray&); +            std::vector<Certificate::ref> getPeerCertificateChain() const override final; +            std::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const override final; -            std::vector<Certificate::ref> getPeerCertificateChain() const; -            std::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; +            virtual ByteArray getFinishMessage() const override final; +            virtual ByteArray getPeerFinishMessage() const override final; -            virtual ByteArray getFinishMessage() const; +            void setX509StoreContext(X509_STORE_CTX *ptr) { x509_store_ctx = ptr; } +            std::function<int (const TLSContext *)> getVerifyCertCallback() { return verifyCertCallback; }          private: +            bool configure(const TLSOptions& options);              static void ensureLibraryInitialized(); - +            static int handleServerNameCallback(SSL *ssl, int *ad, void *arg);              static CertificateVerificationError::Type getVerificationErrorTypeForResult(int); +            void initAndSetBIOs(); +            void doAccept();              void doConnect();              void sendPendingDataToNetwork();              void sendPendingDataToApplication();          private: -            enum State { Start, Connecting, Connected, Error }; +            enum class State { Start, Accepting, Connecting, Connected, Error }; +            const Mode mode_;              State state_; -            SSL_CTX* context_; -            SSL* handle_; -            BIO* readBIO_; -            BIO* writeBIO_; -    }; +            std::unique_ptr<SSL_CTX> context_; +            std::unique_ptr<SSL> handle_; +            BIO* readBIO_ = nullptr; +            BIO* writeBIO_ = nullptr; +            bool abortTLSHandshake_ = false; +            X509_STORE_CTX *x509_store_ctx = nullptr; +            std::function<int (const TLSContext *)> verifyCertCallback = nullptr; +   };  } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp index 9f7b2aa..e332ca8 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp @@ -1,34 +1,60 @@  /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #include <Swiften/TLS/OpenSSL/OpenSSLContextFactory.h> +#include <openssl/bio.h> +#include <openssl/dh.h> +#include <openssl/pem.h> +  #include <Swiften/Base/Log.h>  #include <Swiften/TLS/OpenSSL/OpenSSLContext.h> +#pragma clang diagnostic ignored "-Wshorten-64-to-32" +  namespace Swift {  bool OpenSSLContextFactory::canCreate() const {      return true;  } -TLSContext* OpenSSLContextFactory::createTLSContext(const TLSOptions&) { -    return new OpenSSLContext(); +std::unique_ptr<TLSContext> OpenSSLContextFactory::createTLSContext(const TLSOptions& options, TLSContext::Mode mode) { +    return std::make_unique<OpenSSLContext>(options, mode); +} + +ByteArray OpenSSLContextFactory::convertDHParametersFromPEMToDER(const std::string& dhParametersInPEM) { +    ByteArray dhParametersInDER; + +    auto bio = std::unique_ptr<BIO, decltype(&BIO_free)>(BIO_new(BIO_s_mem()), BIO_free); +    if (bio) { +        BIO_write(bio.get(), dhParametersInPEM.data(), dhParametersInPEM.size()); +        if (auto params = PEM_read_bio_DHparams(bio.get(), nullptr, nullptr, nullptr)) { +            unsigned char* buffer = nullptr; +            auto len = i2d_DHparams(params, &buffer); +            if (len > 0) { +                dhParametersInDER = createByteArray(buffer, static_cast<size_t>(len)); +                free(buffer); +            } +            DH_free(params); + +        } +    } +    return dhParametersInDER;  }  void OpenSSLContextFactory::setCheckCertificateRevocation(bool check) {      if (check) { -        SWIFT_LOG(warning) << "CRL Checking not supported for OpenSSL" << std::endl; +        SWIFT_LOG(warning) << "CRL Checking not supported for OpenSSL";          assert(false);      }  }  void OpenSSLContextFactory::setDisconnectOnCardRemoval(bool check) {      if (check) { -        SWIFT_LOG(warning) << "Smart cards not supported for OpenSSL" << std::endl; +        SWIFT_LOG(warning) << "Smart cards not supported for OpenSSL";      }  } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h index e121a1a..834e479 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h +++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h @@ -1,23 +1,25 @@  /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2019 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once -#include <cassert> +#include <memory>  #include <Swiften/TLS/TLSContextFactory.h>  namespace Swift {      class OpenSSLContextFactory : public TLSContextFactory {          public: -            bool canCreate() const; -            virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions); +            bool canCreate() const override final; +            virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode) override final; + +            virtual ByteArray convertDHParametersFromPEMToDER(const std::string& dhParametersInPEM) override final;              // Not supported -            virtual void setCheckCertificateRevocation(bool b); -            virtual void setDisconnectOnCardRemoval(bool b); +            virtual void setCheckCertificateRevocation(bool b) override final; +            virtual void setDisconnectOnCardRemoval(bool b) override final;      };  } diff --git a/Swiften/TLS/PrivateKey.cpp b/Swiften/TLS/PrivateKey.cpp new file mode 100644 index 0000000..31cac1d --- /dev/null +++ b/Swiften/TLS/PrivateKey.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swiften/TLS/PrivateKey.h> + +namespace Swift { + +PrivateKey::PrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password) : data_(data), password_(password) { +} + +const SafeByteArray& PrivateKey::getData() const { +    return data_; +} + +const boost::optional<SafeByteArray>& PrivateKey::getPassword() const { +    return password_; +} + +} diff --git a/Swiften/TLS/PrivateKey.h b/Swiften/TLS/PrivateKey.h new file mode 100644 index 0000000..332fc48 --- /dev/null +++ b/Swiften/TLS/PrivateKey.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <memory> + +#include <boost/optional.hpp> + +#include <Swiften/Base/SafeByteArray.h> +#include <Swiften/TLS/PrivateKey.h> + +namespace Swift { + +class PrivateKey { +public: +    using ref = std::shared_ptr<PrivateKey>; + +public: +    PrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password = boost::optional<SafeByteArray>()); + +    const SafeByteArray& getData() const; +    const boost::optional<SafeByteArray>& getPassword() const; + +private: +    SafeByteArray data_; +    boost::optional<SafeByteArray> password_; +}; + +} diff --git a/Swiften/TLS/SConscript b/Swiften/TLS/SConscript index 68bf50b..a79e6a1 100644 --- a/Swiften/TLS/SConscript +++ b/Swiften/TLS/SConscript @@ -4,6 +4,7 @@ objects = swiften_env.SwiftenObject([              "Certificate.cpp",              "CertificateFactory.cpp",              "CertificateTrustChecker.cpp", +            "PrivateKey.cpp",              "ServerIdentityVerifier.cpp",              "TLSContext.cpp",              "TLSContextFactory.cpp", @@ -16,8 +17,11 @@ if myenv.get("HAVE_OPENSSL", 0) :              "OpenSSL/OpenSSLContext.cpp",              "OpenSSL/OpenSSLCertificate.cpp",              "OpenSSL/OpenSSLContextFactory.cpp", +            "OpenSSL/OpenSSLCertificateFactory.cpp",          ])      myenv.Append(CPPDEFINES = "HAVE_OPENSSL") +    if myenv["PLATFORM"] == "win32" : +        myenv.Append(CPPDEFINES = "NOMINMAX")  elif myenv.get("HAVE_SCHANNEL", 0) :      swiften_env.Append(LIBS = ["Winscard"])      objects += myenv.SwiftenObject([ diff --git a/Swiften/TLS/Schannel/SchannelContext.cpp b/Swiften/TLS/Schannel/SchannelContext.cpp index 5799157..722fb4a 100644 --- a/Swiften/TLS/Schannel/SchannelContext.cpp +++ b/Swiften/TLS/Schannel/SchannelContext.cpp @@ -5,7 +5,7 @@   */  /* - * Copyright (c) 2012-2016 Isode Limited. + * Copyright (c) 2012-2019 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -16,6 +16,7 @@  #include <WinHTTP.h> /* For SECURITY_FLAG_IGNORE_CERT_CN_INVALID */ +#include <Swiften/Base/Log.h>  #include <Swiften/TLS/CAPICertificate.h>  #include <Swiften/TLS/Schannel/SchannelCertificate.h> @@ -39,13 +40,20 @@ SchannelContext::SchannelContext(bool tls1_0Workaround) : state_(Start), secCont  //------------------------------------------------------------------------  SchannelContext::~SchannelContext() { -    if (myCertStore_) CertCloseStore(myCertStore_, 0); +    SWIFT_LOG(debug) << "Destroying SchannelContext"; +    if (myCertStore_) { +        if (CertCloseStore(myCertStore_, 0) == FALSE) { +            SWIFT_LOG(debug) << "Failed to close the certificate store"; +        } +    }  }  //------------------------------------------------------------------------  void SchannelContext::determineStreamSizes() { -    QueryContextAttributes(contextHandle_, SECPKG_ATTR_STREAM_SIZES, &streamSizes_); +    if (QueryContextAttributes(contextHandle_, SECPKG_ATTR_STREAM_SIZES, &streamSizes_) != SEC_E_OK) { +        SWIFT_LOG(debug) << "QueryContextAttributes failed to determinate the stream size"; +    }  }  //------------------------------------------------------------------------ @@ -267,8 +275,8 @@ void SchannelContext::continueHandshake(const SafeByteArray& data) {          SecBuffer inBuffers[2];          // Provide Schannel with the remote host's handshake data -        inBuffers[0].pvBuffer     = (char*)(&receivedData_[0]); -        inBuffers[0].cbBuffer     = (unsigned long)receivedData_.size(); +        inBuffers[0].pvBuffer = static_cast<char*>(&receivedData_[0]); +        inBuffers[0].cbBuffer = static_cast<unsigned long>(receivedData_.size());          inBuffers[0].BufferType  = SECBUFFER_TOKEN;          inBuffers[1].pvBuffer   = NULL; @@ -483,8 +491,8 @@ void SchannelContext::decryptAndProcessData(const SafeByteArray& data) {          //   contexts. Additionally, a second SECBUFFER_TOKEN type buffer that contains a security token          //   must also be supplied.          // -        inBuffers[0].pvBuffer     = (char*)(&receivedData_[0]); -        inBuffers[0].cbBuffer     = (unsigned long)receivedData_.size(); +        inBuffers[0].pvBuffer = static_cast<char*>(&receivedData_[0]); +        inBuffers[0].cbBuffer = static_cast<unsigned long>(receivedData_.size());          inBuffers[0].BufferType  = SECBUFFER_DATA;          inBuffers[1].BufferType  = SECBUFFER_EMPTY; @@ -578,7 +586,7 @@ void SchannelContext::encryptAndSendData(const SafeByteArray& data) {          outBuffers[0].BufferType = SECBUFFER_STREAM_HEADER;          outBuffers[1].pvBuffer     = &sendBuffer[0] + streamSizes_.cbHeader; -        outBuffers[1].cbBuffer     = (unsigned long)bytesToSend; +        outBuffers[1].cbBuffer     = static_cast<unsigned long>(bytesToSend);          outBuffers[1].BufferType = SECBUFFER_DATA;          outBuffers[2].pvBuffer     = &sendBuffer[0] + streamSizes_.cbHeader + bytesToSend; @@ -645,6 +653,7 @@ std::vector<Certificate::ref> SchannelContext::getPeerCertificateChain() const {      SECURITY_STATUS status = QueryContextAttributes(contextHandle_, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset());      if (status != SEC_E_OK) { +        SWIFT_LOG(debug) << "Error while Querying the Certificate Chain";          return certificateChain;      }      certificateChain.push_back(std::make_shared<SchannelCertificate>(pServerCert)); @@ -678,6 +687,10 @@ ByteArray SchannelContext::getFinishMessage() const {      if (ret == SEC_E_OK) {          return createByteArray(((unsigned char*) bindings.Bindings) + bindings.Bindings->dwApplicationDataOffset + 11 /* tls-unique:*/, bindings.Bindings->cbApplicationDataLength - 11);      } +    else { +        SWIFT_LOG(debug) << "Error while retrieving Finish Message"; +    } +      return ByteArray();  } diff --git a/Swiften/TLS/Schannel/SchannelContextFactory.cpp b/Swiften/TLS/Schannel/SchannelContextFactory.cpp index f78d386..d029730 100644 --- a/Swiften/TLS/Schannel/SchannelContextFactory.cpp +++ b/Swiften/TLS/Schannel/SchannelContextFactory.cpp @@ -5,7 +5,7 @@   */  /* - * Copyright (c) 2015-2016 Isode Limited. + * Copyright (c) 2015-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -23,11 +23,13 @@ bool SchannelContextFactory::canCreate() const {      return true;  } -TLSContext* SchannelContextFactory::createTLSContext(const TLSOptions& tlsOptions) { +std::unique_ptr<TLSContext> SchannelContextFactory::createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode) { +    // TLS server mode is not supported for the SecureTransport backend yet. +    assert(mode == TLSContext::Mode::Client);      SchannelContext* context = new SchannelContext(tlsOptions.schannelTLS1_0Workaround);      context->setCheckCertificateRevocation(checkCertificateRevocation);      context->setDisconnectOnCardRemoval(disconnectOnCardRemoval); -    return context; +    return std::unique_ptr<TLSContext>(context);  }  void SchannelContextFactory::setCheckCertificateRevocation(bool b) { diff --git a/Swiften/TLS/Schannel/SchannelContextFactory.h b/Swiften/TLS/Schannel/SchannelContextFactory.h index 142f193..76ff365 100644 --- a/Swiften/TLS/Schannel/SchannelContextFactory.h +++ b/Swiften/TLS/Schannel/SchannelContextFactory.h @@ -5,13 +5,15 @@   */  /* - * Copyright (c) 2015 Isode Limited. + * Copyright (c) 2015-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once +#include <memory> +  #include <Swiften/TLS/TLSContextFactory.h>  namespace Swift { @@ -20,8 +22,7 @@ namespace Swift {              SchannelContextFactory();              bool canCreate() const; -            virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions); - +            virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode = TLSContext::Mode::Client);              virtual void setCheckCertificateRevocation(bool b);              virtual void setDisconnectOnCardRemoval(bool b); diff --git a/Swiften/TLS/SecureTransport/SecureTransportContext.mm b/Swiften/TLS/SecureTransport/SecureTransportContext.mm index 1ed636b..b4f7842 100644 --- a/Swiften/TLS/SecureTransport/SecureTransportContext.mm +++ b/Swiften/TLS/SecureTransport/SecureTransportContext.mm @@ -1,5 +1,5 @@  /* - * Copyright (c) 2015-2016 Isode Limited. + * Copyright (c) 2015-2019 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -72,14 +72,14 @@ CFArrayRef CreateClientCertificateChainAsCFArrayRef(CertificateWithKey::ref key)              break;          case errSecAuthFailed:              // Password did not work for decoding the certificate. -            SWIFT_LOG(warning) << "Invalid password." << std::endl; +            SWIFT_LOG(warning) << "Invalid password.";              break;          case errSecDecode:              // Other decoding error. -            SWIFT_LOG(warning) << "PKCS12 decoding error." << std::endl; +            SWIFT_LOG(warning) << "PKCS12 decoding error.";              break;          default: -            SWIFT_LOG(warning) << "Unknown error." << std::endl; +            SWIFT_LOG(warning) << "Unknown error.";      }      if (securityError != errSecSuccess) { @@ -110,20 +110,20 @@ SecureTransportContext::SecureTransportContext(bool checkCertificateRevocation)      // set IO callbacks      error = SSLSetIOFuncs(sslContext_.get(), &SecureTransportContext::SSLSocketReadCallback, &SecureTransportContext::SSLSocketWriteCallback);      if (error != noErr) { -        SWIFT_LOG(error) << "Unable to set IO functions to SSL context." << std::endl; +        SWIFT_LOG(error) << "Unable to set IO functions to SSL context.";          sslContext_.reset();      }      error = SSLSetConnection(sslContext_.get(), this);      if (error != noErr) { -        SWIFT_LOG(error) << "Unable to set connection to SSL context." << std::endl; +        SWIFT_LOG(error) << "Unable to set connection to SSL context.";          sslContext_.reset();      }      error = SSLSetSessionOption(sslContext_.get(), kSSLSessionOptionBreakOnServerAuth, true);      if (error != noErr) { -        SWIFT_LOG(error) << "Unable to set kSSLSessionOptionBreakOnServerAuth on session." << std::endl; +        SWIFT_LOG(error) << "Unable to set kSSLSessionOptionBreakOnServerAuth on session.";          sslContext_.reset();      }  } @@ -154,19 +154,19 @@ std::string SecureTransportContext::stateToString(State state) {  }  void SecureTransportContext::setState(State newState) { -    SWIFT_LOG(debug) << "Switch state from " << stateToString(state_) << " to " << stateToString(newState) << "." << std::endl; +    SWIFT_LOG(debug) << "Switch state from " << stateToString(state_) << " to " << stateToString(newState) << ".";      state_ = newState;  }  void SecureTransportContext::connect() { -    SWIFT_LOG_ASSERT(state_ == None, error) << "current state '" << stateToString(state_) << " invalid." << std::endl; +    SWIFT_LOG_ASSERT(state_ == None, error) << "current state '" << stateToString(state_) << " invalid.";      if (clientCertificate_) {          CFArrayRef certs = CreateClientCertificateChainAsCFArrayRef(clientCertificate_);          if (certs) {              std::shared_ptr<CFArray> certRefs(certs, CFRelease);              OSStatus result = SSLSetCertificate(sslContext_.get(), certRefs.get());              if (result != noErr) { -                SWIFT_LOG(error) << "SSLSetCertificate failed with error " << result << "." << std::endl; +                SWIFT_LOG(error) << "SSLSetCertificate failed with error " << result << ".";              }          }      } @@ -174,23 +174,23 @@ void SecureTransportContext::connect() {  }  void SecureTransportContext::processHandshake() { -    SWIFT_LOG_ASSERT(state_ == None || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid." << std::endl; +    SWIFT_LOG_ASSERT(state_ == None || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid.";      OSStatus error = SSLHandshake(sslContext_.get());      if (error == errSSLWouldBlock) {          setState(Handshake);      }      else if (error == noErr) { -        SWIFT_LOG(debug) << "TLS handshake successful." << std::endl; +        SWIFT_LOG(debug) << "TLS handshake successful.";          setState(HandshakeDone);          onConnected();      }      else if (error == errSSLPeerAuthCompleted) { -        SWIFT_LOG(debug) << "Received server certificate. Start verification." << std::endl; +        SWIFT_LOG(debug) << "Received server certificate. Start verification.";          setState(Handshake);          verifyServerCertificate();      }      else { -        SWIFT_LOG(debug) << "Error returned from SSLHandshake call is " << error << "." << std::endl; +        SWIFT_LOG(debug) << "Error returned from SSLHandshake call is " << error << ".";          fatalError(nativeToTLSError(error), std::make_shared<CertificateVerificationError>());      }  } @@ -226,13 +226,13 @@ void SecureTransportContext::verifyServerCertificate() {      OSStatus cssmResult = 0;      switch(trustResult) {          case kSecTrustResultUnspecified: -            SWIFT_LOG(warning) << "Successful implicit validation. Result unspecified." << std::endl; +            SWIFT_LOG(warning) << "Successful implicit validation. Result unspecified.";              break;          case kSecTrustResultProceed: -            SWIFT_LOG(warning) << "Validation resulted in explicitly trusted." << std::endl; +            SWIFT_LOG(warning) << "Validation resulted in explicitly trusted.";              break;          case kSecTrustResultRecoverableTrustFailure: -            SWIFT_LOG(warning) << "recoverable trust failure" << std::endl; +            SWIFT_LOG(warning) << "recoverable trust failure";              error = SecTrustGetCssmResultCode(trust, &cssmResult);              if (error == errSecSuccess) {                  verificationError_ = CSSMErrorToVerificationError(cssmResult); @@ -304,8 +304,8 @@ bool SecureTransportContext::setClientCertificate(CertificateWithKey::ref cert)  }  void SecureTransportContext::handleDataFromNetwork(const SafeByteArray& data) { -    SWIFT_LOG(debug) << std::endl; -    SWIFT_LOG_ASSERT(state_ == HandshakeDone || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid." << std::endl; +    SWIFT_LOG(debug); +    SWIFT_LOG_ASSERT(state_ == HandshakeDone || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid.";      append(readingBuffer_, data); @@ -332,7 +332,7 @@ void SecureTransportContext::handleDataFromNetwork(const SafeByteArray& data) {                      break;                  }                  else { -                    SWIFT_LOG(error) << "SSLRead failed with error " << error << ", read bytes: " << bytesRead << "." << std::endl; +                    SWIFT_LOG(error) << "SSLRead failed with error " << error << ", read bytes: " << bytesRead << ".";                      fatalError(std::make_shared<TLSError>(), std::make_shared<CertificateVerificationError>());                      return;                  } @@ -347,7 +347,7 @@ void SecureTransportContext::handleDataFromNetwork(const SafeByteArray& data) {              }              break;          case Error: -            SWIFT_LOG(debug) << "Igoring received data in error state." << std::endl; +            SWIFT_LOG(debug) << "Igoring received data in error state.";              break;      }  } @@ -358,13 +358,13 @@ void SecureTransportContext::handleDataFromApplication(const SafeByteArray& data      OSStatus error = SSLWrite(sslContext_.get(), data.data(), data.size(), &processedBytes);      switch(error) {          case errSSLWouldBlock: -            SWIFT_LOG(warning) << "Unexpected because the write callback does not block." << std::endl; +            SWIFT_LOG(warning) << "Unexpected because the write callback does not block.";              return;          case errSSLClosedGraceful:          case noErr:              return;          default: -            SWIFT_LOG(warning) << "SSLWrite returned error code: " << error << ", processed bytes: " << processedBytes << std::endl; +            SWIFT_LOG(warning) << "SSLWrite returned error code: " << error << ", processed bytes: " << processedBytes;              fatalError(std::make_shared<TLSError>(), std::shared_ptr<CertificateVerificationError>());      }  } @@ -376,7 +376,7 @@ std::vector<Certificate::ref> SecureTransportContext::getPeerCertificateChain()              typedef boost::remove_pointer<SecTrustRef>::type SecTrust;              std::shared_ptr<SecTrust> securityTrust; -            SecTrustRef secTrust = nullptr;; +            SecTrustRef secTrust = nullptr;              OSStatus error = SSLCopyPeerTrust(sslContext_.get(), &secTrust);              if (error == noErr) {                  securityTrust = std::shared_ptr<SecTrust>(secTrust, CFRelease); @@ -390,7 +390,7 @@ std::vector<Certificate::ref> SecureTransportContext::getPeerCertificateChain()                  }              }              else { -                SWIFT_LOG(warning) << "Failed to obtain peer trust structure; error = " << error << "." << std::endl; +                SWIFT_LOG(warning) << "Failed to obtain peer trust structure; error = " << error << ".";              }      } @@ -402,7 +402,7 @@ CertificateVerificationError::ref SecureTransportContext::getPeerCertificateVeri  }  ByteArray SecureTransportContext::getFinishMessage() const { -    SWIFT_LOG(warning) << "Access to TLS handshake finish message is not part of OS X Secure Transport APIs." << std::endl; +    SWIFT_LOG(warning) << "Access to TLS handshake finish message is not part of OS X Secure Transport APIs.";      return ByteArray();  } @@ -453,42 +453,42 @@ std::shared_ptr<CertificateVerificationError> SecureTransportContext::CSSMErrorT      std::shared_ptr<CertificateVerificationError> error;      switch(resultCode) {          case CSSMERR_TP_NOT_TRUSTED: -            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_NOT_TRUSTED" << std::endl; +            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_NOT_TRUSTED";              error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::Untrusted);              break;          case CSSMERR_TP_CERT_NOT_VALID_YET: -            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_NOT_VALID_YET" << std::endl; +            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_NOT_VALID_YET";              error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::NotYetValid);              break;          case CSSMERR_TP_CERT_EXPIRED: -            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_EXPIRED" << std::endl; +            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_EXPIRED";              error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::Expired);              break;          case CSSMERR_TP_CERT_REVOKED: -            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_REVOKED" << std::endl; +            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_REVOKED";              error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::Revoked);              break;          case CSSMERR_TP_VERIFY_ACTION_FAILED: -            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_VERIFY_ACTION_FAILED" << std::endl; +            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_VERIFY_ACTION_FAILED";              break;          case CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK: -            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK" << std::endl; +            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK";              if (checkCertificateRevocation_) {                  error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::RevocationCheckFailed);              }              break;          case CSSMERR_APPLETP_OCSP_UNAVAILABLE: -            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_OCSP_UNAVAILABLE" << std::endl; +            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_OCSP_UNAVAILABLE";              if (checkCertificateRevocation_) {                  error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::RevocationCheckFailed);              }              break;          case CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE: -            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE" << std::endl; +            SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE";              error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidPurpose);              break;          default: -            SWIFT_LOG(warning) << "unhandled CSSM error: " << resultCode << ", CSSM_TP_BASE_TP_ERROR: " << CSSM_TP_BASE_TP_ERROR << std::endl; +            SWIFT_LOG(warning) << "unhandled CSSM error: " << resultCode << ", CSSM_TP_BASE_TP_ERROR: " << CSSM_TP_BASE_TP_ERROR;              error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError);              break;      } diff --git a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp index 1fac1fb..ac399e1 100644 --- a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp +++ b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2015-2016 Isode Limited. + * Copyright (c) 2015-2019 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -26,8 +26,10 @@ bool SecureTransportContextFactory::canCreate() const {      return true;  } -TLSContext* SecureTransportContextFactory::createTLSContext(const TLSOptions& /* tlsOptions */) { -    return new SecureTransportContext(checkCertificateRevocation_); +std::unique_ptr<TLSContext> SecureTransportContextFactory::createTLSContext(const TLSOptions& /* tlsOptions */, TLSContext::Mode mode) { +    // TLS server mode is not supported for the SecureTransport backend yet. +    assert(mode == TLSContext::Mode::Client); +    return std::unique_ptr<TLSContext>(new SecureTransportContext(checkCertificateRevocation_));  }  void SecureTransportContextFactory::setCheckCertificateRevocation(bool b) { @@ -37,7 +39,7 @@ void SecureTransportContextFactory::setCheckCertificateRevocation(bool b) {  void SecureTransportContextFactory::setDisconnectOnCardRemoval(bool b) {      disconnectOnCardRemoval_ = b;      if (disconnectOnCardRemoval_) { -        SWIFT_LOG(warning) << "Smart cards have not been tested yet" << std::endl; +        SWIFT_LOG(warning) << "Smart cards have not been tested yet";      }  } diff --git a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h index 74c598f..b86639a 100644 --- a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h +++ b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h @@ -1,11 +1,13 @@  /* - * Copyright (c) 2015 Isode Limited. + * Copyright (c) 2015-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once +#include <memory> +  #include <Swiften/TLS/TLSContextFactory.h>  namespace Swift { @@ -17,7 +19,7 @@ class SecureTransportContextFactory : public TLSContextFactory {          virtual bool canCreate() const; -        virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions); +        virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode = TLSContext::Mode::Client);          virtual void setCheckCertificateRevocation(bool b);          virtual void setDisconnectOnCardRemoval(bool b); diff --git a/Swiften/TLS/ServerIdentityVerifier.cpp b/Swiften/TLS/ServerIdentityVerifier.cpp index 226e94b..18ea2aa 100644 --- a/Swiften/TLS/ServerIdentityVerifier.cpp +++ b/Swiften/TLS/ServerIdentityVerifier.cpp @@ -12,7 +12,7 @@  namespace Swift { -ServerIdentityVerifier::ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter) : domainValid(false) { +ServerIdentityVerifier::ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter, bool checkServer) : domainValid(false), checkServer_(checkServer) {      domain = jid.getDomain();      boost::optional<std::string> domainResult = idnConverter->getIDNAEncoded(domain);      if (!!domainResult) { @@ -36,12 +36,14 @@ bool ServerIdentityVerifier::certificateVerifies(Certificate::ref certificate) {      }      hasSAN |= !dnsNames.empty(); +    std::string prefix = (checkServer_) ? "_xmpp-server." : "_xmpp-client."; +      // SRV names      std::vector<std::string> srvNames = certificate->getSRVNames();      for (const auto& srvName : srvNames) {          // Only match SRV names that begin with the service; this isn't required per          // spec, but we're being purist about this. -        if (boost::starts_with(srvName, "_xmpp-client.") && matchesDomain(srvName.substr(std::string("_xmpp-client.").size(), srvName.npos))) { +        if (boost::starts_with(srvName, prefix) && matchesDomain(srvName.substr(prefix.size(), srvName.npos))) {              return true;          }      } @@ -80,15 +82,15 @@ bool ServerIdentityVerifier::matchesDomain(const std::string& s) const {          if (dotIndex != matchDomain.npos) {              matchDomain = matchDomain.substr(dotIndex + 1, matchDomain.npos);          } -        return matchString == matchDomain; +        return boost::iequals(matchString, matchDomain);      }      else { -        return s == encodedDomain; +        return boost::iequals(s, encodedDomain);      }  }  bool ServerIdentityVerifier::matchesAddress(const std::string& s) const { -    return s == domain; +    return boost::iequals(s, domain);  }  } diff --git a/Swiften/TLS/ServerIdentityVerifier.h b/Swiften/TLS/ServerIdentityVerifier.h index f40c683..f2cf46f 100644 --- a/Swiften/TLS/ServerIdentityVerifier.h +++ b/Swiften/TLS/ServerIdentityVerifier.h @@ -18,7 +18,7 @@ namespace Swift {      class SWIFTEN_API ServerIdentityVerifier {          public: -            ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter); +            ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter, bool checkServer=false);              bool certificateVerifies(Certificate::ref); @@ -30,5 +30,6 @@ namespace Swift {              std::string domain;              std::string encodedDomain;              bool domainValid; +            bool checkServer_;      };  } diff --git a/Swiften/TLS/TLSContext.cpp b/Swiften/TLS/TLSContext.cpp index 2763547..fd31c2d 100644 --- a/Swiften/TLS/TLSContext.cpp +++ b/Swiften/TLS/TLSContext.cpp @@ -1,19 +1,53 @@  /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #include <Swiften/TLS/TLSContext.h> +#include <cassert> +  namespace Swift {  TLSContext::~TLSContext() {  } +void TLSContext::accept() { +    assert(false); +} + +void TLSContext::connect(const std::string& /* serverName */) { +    assert(false); +} + +bool TLSContext::setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& /* certificateChain */) { +    assert(false); +    return false; +} + +bool TLSContext::setPrivateKey(const PrivateKey::ref& /* privateKey */) { +    assert(false); +    return false; +} + +bool TLSContext::setDiffieHellmanParameters(const ByteArray& /*parametersInOpenSslDer*/) { +    assert(false); +    return false; +} + +void TLSContext::setAbortTLSHandshake(bool /* abort */) { +    assert(false); +} +  Certificate::ref TLSContext::getPeerCertificate() const {      std::vector<Certificate::ref> chain = getPeerCertificateChain();      return chain.empty() ? Certificate::ref() : chain[0];  } +ByteArray TLSContext::getPeerFinishMessage() const { +    assert(false); +    return ByteArray(); +} +  } diff --git a/Swiften/TLS/TLSContext.h b/Swiften/TLS/TLSContext.h index 79e3485..f2dbdce 100644 --- a/Swiften/TLS/TLSContext.h +++ b/Swiften/TLS/TLSContext.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -15,6 +15,7 @@  #include <Swiften/TLS/Certificate.h>  #include <Swiften/TLS/CertificateVerificationError.h>  #include <Swiften/TLS/CertificateWithKey.h> +#include <Swiften/TLS/PrivateKey.h>  #include <Swiften/TLS/TLSError.h>  namespace Swift { @@ -23,9 +24,21 @@ namespace Swift {          public:              virtual ~TLSContext(); +            virtual void accept();              virtual void connect() = 0; +            virtual void connect(const std::string& serverName); + +            virtual bool setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& /* certificateChain */); +            virtual bool setPrivateKey(const PrivateKey::ref& /* privateKey */);              virtual bool setClientCertificate(CertificateWithKey::ref cert) = 0; +            virtual bool setDiffieHellmanParameters(const ByteArray& parametersInOpenSslDer); + +            /** +             *  This method can be used during the \ref onServerNameRequested signal, +             *  to report an error about an unknown host back to the requesting client. +             */ +            virtual void setAbortTLSHandshake(bool /* abort */);              virtual void handleDataFromNetwork(const SafeByteArray&) = 0;              virtual void handleDataFromApplication(const SafeByteArray&) = 0; @@ -35,11 +48,19 @@ namespace Swift {              virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const = 0;              virtual ByteArray getFinishMessage() const = 0; +            virtual ByteArray getPeerFinishMessage() const; + +       public: +            enum class Mode { +                Client, +                Server +            };          public:              boost::signals2::signal<void (const SafeByteArray&)> onDataForNetwork;              boost::signals2::signal<void (const SafeByteArray&)> onDataForApplication;              boost::signals2::signal<void (std::shared_ptr<TLSError>)> onError;              boost::signals2::signal<void ()> onConnected; +            boost::signals2::signal<void (const std::string&)> onServerNameRequested;      };  } diff --git a/Swiften/TLS/TLSContextFactory.cpp b/Swiften/TLS/TLSContextFactory.cpp index d196e15..91e60d6 100644 --- a/Swiften/TLS/TLSContextFactory.cpp +++ b/Swiften/TLS/TLSContextFactory.cpp @@ -6,9 +6,16 @@  #include <Swiften/TLS/TLSContextFactory.h> +#include <cassert> +  namespace Swift {  TLSContextFactory::~TLSContextFactory() {  } +ByteArray TLSContextFactory::convertDHParametersFromPEMToDER(const std::string& /* pem */) { +    assert(false); +    return ByteArray(); +} +  } diff --git a/Swiften/TLS/TLSContextFactory.h b/Swiften/TLS/TLSContextFactory.h index d2ffe15..5f70541 100644 --- a/Swiften/TLS/TLSContextFactory.h +++ b/Swiften/TLS/TLSContextFactory.h @@ -1,25 +1,29 @@  /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once +#include <memory> +  #include <Swiften/Base/API.h> +#include <Swiften/Base/ByteArray.h> +#include <Swiften/TLS/TLSContext.h>  #include <Swiften/TLS/TLSOptions.h>  namespace Swift { -    class TLSContext; -      class SWIFTEN_API TLSContextFactory {          public:              virtual ~TLSContextFactory();              virtual bool canCreate() const = 0; -            virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions) = 0; +            virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode = TLSContext::Mode::Client) = 0;              virtual void setCheckCertificateRevocation(bool b) = 0;              virtual void setDisconnectOnCardRemoval(bool b) = 0; + +            virtual ByteArray convertDHParametersFromPEMToDER(const std::string& pem);      };  } diff --git a/Swiften/TLS/TLSError.h b/Swiften/TLS/TLSError.h index ae775e6..9e4af2f 100644 --- a/Swiften/TLS/TLSError.h +++ b/Swiften/TLS/TLSError.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2012-2016 Isode Limited. + * Copyright (c) 2012-2019 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -7,6 +7,7 @@  #pragma once  #include <memory> +#include <string>  #include <Swiften/Base/API.h>  #include <Swiften/Base/Error.h> @@ -18,16 +19,23 @@ namespace Swift {              enum Type {                  UnknownError, -                CertificateCardRemoved +                CertificateCardRemoved, +                AcceptFailed, +                ConnectFailed              }; -            TLSError(Type type = UnknownError) : type(type) {} +            TLSError(Type type = UnknownError, std::string message = "") : type_(type), message_(std::move(message)) {}              Type getType() const { -                return type; +                return type_; +            } + +            const std::string& getMessage() const { +                return message_;              }          private: -            Type type; +            Type type_; +            std::string message_;      };  } diff --git a/Swiften/TLS/TLSOptions.h b/Swiften/TLS/TLSOptions.h index dd7e920..e3faaf9 100644 --- a/Swiften/TLS/TLSOptions.h +++ b/Swiften/TLS/TLSOptions.h @@ -7,8 +7,11 @@  #pragma once  namespace Swift { +    class TLSContext; +    class Certificate; -    struct TLSOptions { +    class TLSOptions { +      public:          TLSOptions() : schannelTLS1_0Workaround(false) {          } @@ -21,5 +24,54 @@ namespace Swift {           */          bool schannelTLS1_0Workaround; +        /** +         * OpenSSL configuration flags +         */ +        boost::optional<bool> workaroundMicrosoftSessID; +        boost::optional<bool> workaroundNetscapeChallenge; +        boost::optional<bool> workaroundNetscapeReuseCipherChange; +        boost::optional<bool> workaroundSSLRef2ReuseCertType; +        boost::optional<bool> workaroundMicrosoftBigSSLv3Buffer; +        boost::optional<bool> workaroundSSLeay080ClientDH; +        boost::optional<bool> workaroundTLSD5; +        boost::optional<bool> workaroundTLSBlockPadding; +        boost::optional<bool> workaroundDontInsertEmptyFragments; +        boost::optional<bool> workaroundAll; +        boost::optional<bool> suppressSSLv2; +        boost::optional<bool> suppressSSLv3; +        boost::optional<bool> suppressTLSv1; +        boost::optional<bool> disableTLSRollBackBug; +        boost::optional<bool> singleDHUse; + +        /** +         * Other OpenSSL configuration items +         */ +        boost::optional<std::string> cipherSuites; +        boost::optional<std::string> context; +        boost::optional<int> sessionCacheTimeout; +        boost::optional<int> verifyDepth; + +        enum class VerifyMode { +            None, +            Required, +            Optional +        }; +        boost::optional<VerifyMode> verifyMode; + +        /** +         * Callback for certificate verification +         */ + +        boost::optional<std::function<int(const TLSContext *)>> verifyCertificateCallback; + +        /** +         * Allows specification of application-specific Trust Anchors +         */ +        boost::optional<std::vector<std::shared_ptr<Certificate>>> trustAnchors; + +        /** +         * Turns off automatic loading of system Trust Anchors +         */ +        bool ignoreSystemTrustAnchors = false;      };  } diff --git a/Swiften/TLS/UnitTest/CertificateTest.cpp b/Swiften/TLS/UnitTest/CertificateTest.cpp index 2483dae..d5bf210 100644 --- a/Swiften/TLS/UnitTest/CertificateTest.cpp +++ b/Swiften/TLS/UnitTest/CertificateTest.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010-2016 Isode Limited. + * Copyright (c) 2010-2018 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -10,9 +10,12 @@  #include <cppunit/extensions/TestFactoryRegistry.h>  #include <Swiften/Base/ByteArray.h> +#include <Swiften/Base/Platform.h>  #include <Swiften/Crypto/CryptoProvider.h>  #include <Swiften/Crypto/PlatformCryptoProvider.h>  #include <Swiften/TLS/Certificate.h> +#include <Swiften/TLS/CertificateFactory.h> +#include <Swiften/TLS/PlatformTLSFactories.h>  #include <Swiften/TLS/SimpleCertificate.h>  using namespace Swift; @@ -20,6 +23,10 @@ using namespace Swift;  class CertificateTest : public CppUnit::TestFixture {          CPPUNIT_TEST_SUITE(CertificateTest);          CPPUNIT_TEST(testGetSHA1Fingerprint); +#ifdef HAVE_OPENSSL +        CPPUNIT_TEST(testParsingPEMCertificate); +#endif +        CPPUNIT_TEST(testParsingPEMPrivateKey);          CPPUNIT_TEST_SUITE_END();      public: @@ -29,6 +36,108 @@ class CertificateTest : public CppUnit::TestFixture {              CPPUNIT_ASSERT_EQUAL(std::string("2f:b5:e1:34:19:fc:89:24:68:65:e7:a3:24:f4:76:ec:62:4e:87:40"), Certificate::getSHA1Fingerprint(testling, std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()).get()));          } + +        void testParsingPEMCertificate() { +            PlatformTLSFactories tlsFactories; + +            auto PEMCertificate = R"(-----BEGIN CERTIFICATE----- +MIIEsjCCApoCCQCbkjlQfUqPtTANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDDBBt +b250YWd1ZS5leGFtcGxlMB4XDTE4MDExODE2NTMxMloXDTQ1MDYwNDE2NTMxMlow +GzEZMBcGA1UEAwwQbW9udGFndWUuZXhhbXBsZTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBALAx5xuEYOjDJ9QHMvwRuDzxbHl1zR97SJFPAkmD8xH0sC61 +DNRyUvRq6UXb4znhqeqrNuZ9PV47GyK2Dpy/c/MY5NE3m/c+Z1tUnrcqyCkxITIn +jdSI/elc9yhtMXX6lRi68BdPJnj/9+6trW0cWHlKEgnaSTAgCVB+4Dg9mjTIroCa +HLoAKhr2zS7Ihs28aWb0cSvZ+qFgQhr7FGP2kfssTTQYyRPn9uHhtyWH6RqSv5x9 +BBGZWN5GtjkJvupcYWLt8ftuQyFpwEeEz5cgtiyKgsfz9CYvS9/OcwdLQr4z5Zq6 +eKxsog9GdwIi1Us4KR0X6tKu9ze42iEWF+foEWFP9/bjrVK/tt5lTSKnenk0nA8I +amkG0btNAGihDti3lv60bGpd3B2/uh4gzzT2buXdf0EaybCt52MIr8xkwMU0Tkri +RAYchdS8U8sekWG5Lg9m3L2BDa8/TKS/WflJhGcZeInGQslgIx7fCgO1M7Zr50pO +wSURPXpvqUkXNEBy639UQEUsnBhntEQwZWx/6x6Ma/U5a5dL6qbtEJjlwIvS+nl9 +3w26g3DvWydNMCtZIVhgdrl+dZs+Uw5eA3QkHkDTSfYvQk7X5SYL0J5ZxwBvU9r1 +ED054+TAEuX2euiRA37xLhxonj8BaKkPQGlAHCLZaZPmNJWkNxElJhMoCfqBAgMB +AAEwDQYJKoZIhvcNAQELBQADggIBAF+FNyW3nVeQeugBMksIhj7EMJl1AEKi0+78 +ZPsYX3CDbc/8GRZoTg/EWSiwPCBYc9VsxuKtODEYABCZgk7LnSBYoEauJDKWqkgM +UOKPJI2hu7mIK7FJpjvEZe2MnRRA63oI/NVDJm8T2clrv/vPkY+ppsVl0toC0SpH +/3dF5c65vYI19rTJraRU6kIrrgxFBzxzpn07LGh2rrOCQfy2umTSRMwz3ORAFfmi ++Kek1Dt7c+JVJ0ivCwhhc8MKza3JS2MuDfVWGnXtDLb81Ai0t4tQfLKvZEcgW+lh +Drz9gv22buwncWL/IxtuhzyILtDSDKAYFbhfG6IAQut9BjMgpMnKrBCDlOLJl08K +tgj2h7vUKyNSt3ndcSAtXjr6FD7+xPExJuyn/MgLONGGAZoZHFB4QO90wQaXxMPh +7rnjUtzfLR8qkDmX8ZB4f4VOWpDWo4hBpgjTk0gYfzEkrh+zTqE9reh7CZ1WzwXO +KnIBU2dZOE+XsJe49lW106DLqGzKRuQMUAwFMb7C7Nlg9GKTnvi41o+g6YE+MgxR +uPKu891pCBtnDxZiWPT+7Fa/9UXxdIOTVIHW6utSWiWYbeNwXSmIm2ShfmNfWj9m +x1JgJrFB6daWGR9aDBeDVRhgL6Z35lH7xI62pLJ4o2d2Y/9dUWAJfz5O8opeCyrF +zqCzpwGL +-----END CERTIFICATE----- +)"; + +            auto certificateChain = tlsFactories.getCertificateFactory()->createCertificateChain(createByteArray(PEMCertificate)); +            CPPUNIT_ASSERT_EQUAL(size_t(1), certificateChain.size()); + +            auto certificate = certificateChain[0].get(); +            CPPUNIT_ASSERT_EQUAL(std::string("/CN=montague.example"), certificate->getSubjectName()); +        } + +        void testParsingPEMPrivateKey() { +            PlatformTLSFactories tlsFactories; + +            auto PEMPrivateKey = R"(-----BEGIN PRIVATE KEY----- +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 +5IMXuORWuoq0gXiCdEyko+saXsyWlqw= +-----END PRIVATE KEY----- +)"; + +            auto privateKey = tlsFactories.getCertificateFactory()->createPrivateKey(createSafeByteArray(PEMPrivateKey)); + +            CPPUNIT_ASSERT(privateKey != nullptr); +        }  };  CPPUNIT_TEST_SUITE_REGISTRATION(CertificateTest); diff --git a/Swiften/TLS/UnitTest/ClientServerTest.cpp b/Swiften/TLS/UnitTest/ClientServerTest.cpp new file mode 100644 index 0000000..a356dcf --- /dev/null +++ b/Swiften/TLS/UnitTest/ClientServerTest.cpp @@ -0,0 +1,883 @@ +/* + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <map> +#include <memory> +#include <utility> +#include <vector> + +#include <boost/variant.hpp> + +#include <gtest/gtest.h> + +#include <Swiften/Base/Log.h> +#include <Swiften/TLS/CertificateFactory.h> +#include <Swiften/TLS/PlatformTLSFactories.h> +#include <Swiften/TLS/TLSContext.h> +#include <Swiften/TLS/TLSContextFactory.h> +#include <Swiften/TLS/TLSOptions.h> + +using namespace Swift; +namespace { + + +std::map<std::string, std::string> certificatePEM = { +    {"montague.example", +R"(-----BEGIN CERTIFICATE----- +MIIEsjCCApoCCQCbkjlQfUqPtTANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDDBBt +b250YWd1ZS5leGFtcGxlMB4XDTE4MDExODE2NTMxMloXDTQ1MDYwNDE2NTMxMlow +GzEZMBcGA1UEAwwQbW9udGFndWUuZXhhbXBsZTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBALAx5xuEYOjDJ9QHMvwRuDzxbHl1zR97SJFPAkmD8xH0sC61 +DNRyUvRq6UXb4znhqeqrNuZ9PV47GyK2Dpy/c/MY5NE3m/c+Z1tUnrcqyCkxITIn +jdSI/elc9yhtMXX6lRi68BdPJnj/9+6trW0cWHlKEgnaSTAgCVB+4Dg9mjTIroCa +HLoAKhr2zS7Ihs28aWb0cSvZ+qFgQhr7FGP2kfssTTQYyRPn9uHhtyWH6RqSv5x9 +BBGZWN5GtjkJvupcYWLt8ftuQyFpwEeEz5cgtiyKgsfz9CYvS9/OcwdLQr4z5Zq6 +eKxsog9GdwIi1Us4KR0X6tKu9ze42iEWF+foEWFP9/bjrVK/tt5lTSKnenk0nA8I +amkG0btNAGihDti3lv60bGpd3B2/uh4gzzT2buXdf0EaybCt52MIr8xkwMU0Tkri +RAYchdS8U8sekWG5Lg9m3L2BDa8/TKS/WflJhGcZeInGQslgIx7fCgO1M7Zr50pO +wSURPXpvqUkXNEBy639UQEUsnBhntEQwZWx/6x6Ma/U5a5dL6qbtEJjlwIvS+nl9 +3w26g3DvWydNMCtZIVhgdrl+dZs+Uw5eA3QkHkDTSfYvQk7X5SYL0J5ZxwBvU9r1 +ED054+TAEuX2euiRA37xLhxonj8BaKkPQGlAHCLZaZPmNJWkNxElJhMoCfqBAgMB +AAEwDQYJKoZIhvcNAQELBQADggIBAF+FNyW3nVeQeugBMksIhj7EMJl1AEKi0+78 +ZPsYX3CDbc/8GRZoTg/EWSiwPCBYc9VsxuKtODEYABCZgk7LnSBYoEauJDKWqkgM +UOKPJI2hu7mIK7FJpjvEZe2MnRRA63oI/NVDJm8T2clrv/vPkY+ppsVl0toC0SpH +/3dF5c65vYI19rTJraRU6kIrrgxFBzxzpn07LGh2rrOCQfy2umTSRMwz3ORAFfmi ++Kek1Dt7c+JVJ0ivCwhhc8MKza3JS2MuDfVWGnXtDLb81Ai0t4tQfLKvZEcgW+lh +Drz9gv22buwncWL/IxtuhzyILtDSDKAYFbhfG6IAQut9BjMgpMnKrBCDlOLJl08K +tgj2h7vUKyNSt3ndcSAtXjr6FD7+xPExJuyn/MgLONGGAZoZHFB4QO90wQaXxMPh +7rnjUtzfLR8qkDmX8ZB4f4VOWpDWo4hBpgjTk0gYfzEkrh+zTqE9reh7CZ1WzwXO +KnIBU2dZOE+XsJe49lW106DLqGzKRuQMUAwFMb7C7Nlg9GKTnvi41o+g6YE+MgxR +uPKu891pCBtnDxZiWPT+7Fa/9UXxdIOTVIHW6utSWiWYbeNwXSmIm2ShfmNfWj9m +x1JgJrFB6daWGR9aDBeDVRhgL6Z35lH7xI62pLJ4o2d2Y/9dUWAJfz5O8opeCyrF +zqCzpwGL +-----END CERTIFICATE----- +)"}, +    {"capulet.example", +R"(-----BEGIN CERTIFICATE----- +MIIEsDCCApgCCQDUGdmqHfGngTANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9j +YXB1bGV0LmV4YW1wbGUwHhcNMTgwMTE4MTY1NjEyWhcNNDUwNjA0MTY1NjEyWjAa +MRgwFgYDVQQDDA9jYXB1bGV0LmV4YW1wbGUwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCgA/CpV7UGqUdjhEVoMjFIZ6Ca/C7utrVPAqBvE14eiurIhQrQ +AmYeC9zA4/uXCdqkGq/a/RbF3OOCKLRUejCcynb0EnxiHxHa48ZsvLbKCK6guoXE +pWnaZsmRpvJrBB2z6ohmxRuaDuc5CJT+Oq8AFPp3StTAFDo3Cju3fsGZPkNpCGbj +ELwk4ok9INtEuTrMEmHZTD+VfjpXauUfN4ygKaPXJanCuxuifyin403BWgd8igkD +oSCWxoDFMy3HGbh/NU+nJPJ23FxVC39RdDG3elvjNFpSZnALbiMnyor7YjF5TN93 +1ZWwn3VTnl7DnFquEbKbYyVFUzRII8Cd1TzKOL48rVLrCAqMXwm6YFtST6qPb9TZ +0SX8qZGCwBfHV5OeS0ZgiBMMlmUPYcw9MlyvZnYyDPCOoPWmhEqd2gQzn//7hzb5 +mriCEyfcMzAqohylBNHXUVZTx5KcazJz6oOYdWEs1jfSXNKefgSWlgeoG2fgTXPN +1OkQVS+FOiI0VCAIwR+vxhG3hVTz3kzXWvEt7M51faaHWWlnSzOrbSuj3f0ibS5J +cj6ClyzOQRQDwzofyZ7oPWh6No/XkepVIn3HTTlnj1/8e6VsH+EBKSzoX2XvWPkO +GAZEGHKiKh944u6d6mW37BPD2oKyusP3uPL5j2Fdm+m0HkP3/7yw+5EFVQIDAQAB +MA0GCSqGSIb3DQEBCwUAA4ICAQCfCGK4iDo8H0i12wBC0+6GB9NBmgn8P09AUN0l +tQjZPaqZCtLtBBqOrAaZQBQMfI0QAm5/h/VkhMvz5HQjqmQhI2drSDOmarYzOGgY +yVItkEwtLEVhiGoeJ+L3Smosoqq6QFjcDzH7UzPTrJNspnY+09+m53pJggh41zzt +1TOU+QasaB5oGopFaFUUlwjHAc7FpZgEd+tV6j0cpdT3GabVkkoLg01Z+0mqkpLD +OjRBKJX8XvZ38VESsy3gWpcXnDq03n+8OgZo4R9SEcyyxjWbyb+qg2dzbQvRB2Va +QNoXp5EzemXvFSulhR+TfDk2K1h45BurikRQxDi8LpBTUsCMwiqXdem68HOlTwLi +/kMWbnVBcdurYcWVSwlJU4EJcTEdk51JStO1V0nAA0nCwn/iEhY8I6BitnrcCJ5e +4CGVWr+zAm8DBjaFMTzy46Q5NcT0hwnHGN6T6l4aMcRggIK9anRbXCn6dSzma1pd +R5N/Do00FTpyZGcUlVPnSlIfZVl7y/9XEO1n6xDJURrefL1JrM7UMyB17jA8HMq3 +S05kF7XRpludRB4QkAJt5BNNv6BPP7HPIKyR/rq94ONvzVPAo7uASyFE2sMBsfwP +pXAI1LVolPCoUC13jEkKdmc8kMSxU+XtsvFryNhkfQtZfSg+nBRFYptFE7GrZ9WY +GMSL4g== +-----END CERTIFICATE----- +)"}, +    {"casigned.example.com", +R"(-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIJAPEpYlUWlJW8MA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV +BAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ4wDAYDVQQKDAVJc29kZTEPMA0GA1UE +AwwGVGVzdENBMB4XDTE4MDExODAwMDAwMFoXDTQ5MDExODAwMDAwMFowTjELMAkG +A1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxDjAMBgNVBAoMBUlzb2RlMR0wGwYD +VQQDDBRjYXNpZ25lZC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAKw4iCscGIDCc55mwxgJsn/v2GEorpE9zHYsMfl6px2aRLGgB8ki +xYyNiEPA/b/ilShRp7pp22LPb5O3aOVVWzAs5FTvPdFY2vS8YIxR6XSKXO3u4Q8/ +w8CR0AbKpeCtL0TwSl1u76nq6ORMep6QGsTjDLQ7wUwdShL4qV4nIGxJWon/5GI0 +nl4Xr/wzmysbMjAMSiWOR90DNZMvqQBBhQcJdqdirc+whR8gOhIMvJBn5jlMuFEB +zL/bR+kG/zFuD0EMsMTy6TETefYuxeWmhuzQENyAV5+1v48G/1a9zzN9Y0xQ6T0N +ppbLzq+/zhxT1eBN8/O0bAGUzzk1VSFvouECAwEAAaNQME4wHQYDVR0OBBYEFCnR +umlxjdaUvcpf77zahrDmPnt1MB8GA1UdIwQYMBaAFAbbhPAKtD5YzxQqGH2Um4/f +OmdFMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEPrTTKS+7A4dJJh +gSAS44ZYyR8/kt9Y3V4qxJM2xrOrW6rM8N15n2i/osSy0AYHiPuvgDG+D7hLt5Ct +7jZKrp3JRsBaU8//9k6+euwVyWS+EKo/eb7qtSNibZOk7GwrXekJMbRXz1cHTKnW +0R8UG+EMkOqKQh5dhWuEBXff9SYLTALf0/i+/Ixl3b9rQ6zpY/7UwBdlWLaliM1i +odIgVu1XRE6Su+bGMa1L5ArniN0LMFq9Mxag2H0V7Kru7vIvbBkL7U6tbi7u26hv +q4kUFROW2U2C02FTmJeSEBvWoDyMoVjKkmaTL0+vDSQRPGtCsvCgaCXgOwU2Po9s +8jjHQxg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIJAPEpYlUWlJW7MA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV +BAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ4wDAYDVQQKDAVJc29kZTEPMA0GA1UE +AwwGVGVzdENBMB4XDTE4MDExODAwMDAwMFoXDTQ5MDExODAwMDAwMFowQDELMAkG +A1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxDjAMBgNVBAoMBUlzb2RlMQ8wDQYD +VQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiCA+9 +bdSHFNPOgbsnhrKbLJP+1lxWTe06+ExwJA3QgCKsR13MsAK8rhmuNB0k8ZIFJL+c +yUOUgw0370eP940QAtK+HinJpvaGEZKEwaB8VUCjzFTb7zUBio1Y7B45aawEV3qT +Lzcnil3f7vPIxnAoaptzPriySdggIsimj+y6AWum5FUPyfZzF7EnYfF/VH8V/ZNX +fku7PPbaeDHvu4EF/0s8P+/l6jddz8dqbDMjH3GDM8sjryWPQPNDjKZ1x80BIdyq +s9m/kXqIIySNwdIa/X+nYTMchUa5y46n2N4n0RA4F+rf+Ni1Cxk9Ejmmz5hinV+t +Yzh6jsbB7yZiqEnzAgMBAAGjUDBOMB0GA1UdDgQWBBQG24TwCrQ+WM8UKhh9lJuP +3zpnRTAfBgNVHSMEGDAWgBQG24TwCrQ+WM8UKhh9lJuP3zpnRTAMBgNVHRMEBTAD +AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBMHQ3WMEhkEc0VoOEw3mbsngt1eyJz+nFM +FeGdRb7JSwpJfe/yBxyGodGyHsQJPVole6sUdq8S1QGT547PkgDHc6Btoq96Mahq +brgfx1JL9a1F8qaqcENebHc7ltW8kps4xtdlithfo4nKaciYZ5mKbug+AFv569mb +LU3F282dyW1dLa7+L8dGbb8Ntpnp0BB0Cotm4GX3Xi6Y+fikouqcmXj7vX+uMhG4 +pQpXqb2ML1Gev4w6XUsnls+OJok97x99NsOcqtwqsLJS/sg0cpwrgZ9+50hQKlHO +8H6hhNjGBkvpIiNsWb9UOc+Id86J+drs9Ed93Eyyvwnq2XcqPGF3 +-----END CERTIFICATE-----)"}}; +std::map<std::string, std::string> privateKeyPEM = { +    {"montague.example", +R"(-----BEGIN PRIVATE KEY----- +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 +5IMXuORWuoq0gXiCdEyko+saXsyWlqw= +-----END PRIVATE KEY----- +)"},{"capulet.example", +R"(-----BEGIN PRIVATE KEY----- +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 +dwiNVl130YWaJLjiA9Poc2llKtypfQ8= +-----END PRIVATE KEY----- +)"}, +    {"casigned.example.com", +R"(-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsOIgrHBiAwnOe +ZsMYCbJ/79hhKK6RPcx2LDH5eqcdmkSxoAfJIsWMjYhDwP2/4pUoUae6adtiz2+T +t2jlVVswLORU7z3RWNr0vGCMUel0ilzt7uEPP8PAkdAGyqXgrS9E8Epdbu+p6ujk +THqekBrE4wy0O8FMHUoS+KleJyBsSVqJ/+RiNJ5eF6/8M5srGzIwDEoljkfdAzWT +L6kAQYUHCXanYq3PsIUfIDoSDLyQZ+Y5TLhRAcy/20fpBv8xbg9BDLDE8ukxE3n2 +LsXlpobs0BDcgFeftb+PBv9Wvc8zfWNMUOk9DaaWy86vv84cU9XgTfPztGwBlM85 +NVUhb6LhAgMBAAECggEAP5KesUstwFoHU/GbEsSBn7hpp3Pc/MBTOUwXJoCgL81C +jk8EkLrFMZ0NL/WNtcduR/PcdnOdCFyKU3zgu8dGYgIwRyoJJ74lFTJ0cVfLVbm9 +lBcZ/QcMu4kXwWoM0WMcj9YjnVnWHmMYlS2mFeIb/5HQnrdwIyisr1QbMSZAUbgn +HOLSjkd0KUcXxwP9HfE/8zUFbUsv/wPHE9T5eWqZz7lKZRvgUyk3jYElpewxqPvR +HPOq9BN87ft7rPzmmZ4JCU2tM5MEQONo3jT5pljTU/iz4pfs1HArHS+/zZbGcoiu +AhODErhLucodwZ07MZsE9KAdGPGUeob4SgMwmkIiAQKBgQDYZ33DHOgYd5Fa7Tqv +TC77Nbpjmnk5lyoInNf+7pxBTgqIuKbpS6Ctq76nHI0qerNfd44LqojQEJ9S5rbL +3s+HR+pypx3DyysE92uLaVsF4rfwZeEpJyQ1Q9NGbsKVb2nNrdGs3LZhG5PL+JQT +8lS9h/sMIbh5Gd/Dkt8a2HlTawKBgQDLu3YdXWeWUOcq63YrLFDO+RUGsCe1cf+2 +3wTmMRgxfzpAPDZKymUPwrJNd1ddCQiRVKg91pLhlDl8B89Q4iWdOpYnE6vrnw3w +6iEdEyA6IauvEvD3JPXHGtVHKBS/wz06wQVGzsgMvVun1w32SDzTGZ/somvkN1UE +BTMRVqXB4wKBgCgkgnq4GQG45FjGmtU7v5dP7yRc2lHpjZ2NYq/imU+v51pXTncG +VWcB7drX/AO+QRnbdpkJaFmYS8W4EH6e9UY1+/KKt4XDYVaJgwSYZY8g8FgCvhLB +NQFAdKejEitgQHMr5DI5uwcmc3MpyGYu1LNXzyT/+FVWjGCeFQ7IyzDDAoGAXK9I ++ez/bkQaFs2OMe/0R2TL6wIC3qohMpndSbf2ELaX0pdR/856EaR6p4FujlXIvw0r +iQ5wPg3H5SRMbbikchYwL1DkusnedsadD/QeijO9PmW4mEcJRW5wrvGOFvuEhJFB +jEgQfLM82muc33lXqpYSyn3N5cJCfBS9edrYiA8CgYEA0Tsa4KvwzmBXzy2jexUT +PPYtV9S7hS0D7Owf5SBI1wtWczOu3Y6DsCG4mbDi/tvo1dxC9d5Ns8RXHH9mtdyn +8VUuSu24lk7VZ0OP5/byiAGqC2urDFGe0iBqePdb+3uS1g132LzAJm4RqBYw4Xjd +P6n9b0V3iodcFLMTy1ShJ+I= +-----END PRIVATE KEY----- +)"}}; + +auto montagueEncryptedPEM = R"(-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,6B0A69362920334824667C1B6207279B + +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 +-----END RSA PRIVATE KEY----- +)"; + +auto dhParamsOpenSslDer1024 = R"(-----BEGIN DH PARAMETERS----- +MIGHAoGBANjw4f5+gu8b8X8O6ALyJA1tH9TQTWZEI3YjUKQ1m0dVBMJ6XDC7FLJn +gqE4hIGcm1FAWwIiuo0uMufqyVwFT2c+G8j4JHWK5z1tEP+GaqiO34N0cUo86qHp +THSkJN7LuHbYRqI9fHWDZocW/5yAsDq5RPUCjFZAoh1BWdfDFfrjAgEC +-----END DH PARAMETERS----- +)"; +auto dhParamsOpenSslDer2048 = R"(-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA0Q6vD5qtrh3XEkVkbN29ord/k3sgo2Q3PiFmyFt89qqVbebEzYmt +t8DwaFGMcGlyKs4Lb1s7vocm9y3M9C0FZm85Muvv8WCbLZVZ+wfEtMibOjgRoDqt +p7Qqe7/iPgMVrSjWegVkg3V8K8dnPpohPClM0yOe4NpBjSVNgBVJRpEtH8gFiCor +H7hw63HpN/MgFdkjZNeCN+erv8p673xH8LrN98gQpkdQ9vCqYt1dHxF2XZcxBp8x +XganwPeGgQosofkA6nVB70hyjwjEyxnHJZIMlx6DPXWC7X6ed0SazgH0sQNdACvG +uU1zHCVIv6/f0adKrJg0s1jrM3qWZ6HmUwIBAg== +-----END DH PARAMETERS----- +)"; + +auto createTLSContext = [](TLSContext::Mode mode) { +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    auto tlsContextFactory = tlsFactories->getTLSContextFactory(); + +    auto tlsContext = std::unique_ptr<TLSContext>(tlsContextFactory->createTLSContext({}, mode)); +    return tlsContext; +}; + +// This connects a TLSContext to another TLSContext +class ClientServerConnector { +    public: +        ClientServerConnector(TLSContext* client, TLSContext* server) : clientContext_(client), serverContext_(server) { +            connections_.push_back(clientContext_->onDataForNetwork.connect([&](const SafeByteArray& data) { +                serverContext_->handleDataFromNetwork(data); +            })); +            connections_.push_back(serverContext_->onDataForNetwork.connect([&](const SafeByteArray& data) { +                clientContext_->handleDataFromNetwork(data); +            })); +        } + +    private: +        TLSContext* clientContext_; +        TLSContext* serverContext_; +        std::vector<boost::signals2::connection> connections_; +}; + +struct TLSDataForNetwork { +    SafeByteArray data; +}; + +struct TLSDataForApplication { +    SafeByteArray data; +}; + +struct TLSFault { +    std::shared_ptr<Swift::TLSError> error; +}; + +struct TLSConnected { +    std::vector<Certificate::ref> chain; +}; + +struct TLSServerNameRequested { +    std::string name; +}; + +using TLSEvent = boost::variant<TLSDataForNetwork, TLSDataForApplication, TLSFault, TLSConnected, TLSServerNameRequested>; + +class TLSEventToSafeByteArrayVisitor : public boost::static_visitor<SafeByteArray> { +    public: +        SafeByteArray operator()(const TLSDataForNetwork& tlsData) const { +            return tlsData.data; +        } + +        SafeByteArray operator()(const TLSDataForApplication& tlsData) const { +            return tlsData.data; +        } + +        SafeByteArray operator()(const TLSFault&) const { +            return createSafeByteArray(""); +        } + +        SafeByteArray operator()(const TLSConnected&) const { +            return createSafeByteArray(""); +        } + +        SafeByteArray operator()(const TLSServerNameRequested&) const { +            return createSafeByteArray(""); +        } +}; + +class TLSEventToStringVisitor : public boost::static_visitor<std::string> { +    public: +        std::string operator()(const TLSDataForNetwork& event) const { +            return std::string("TLSDataForNetwork(") + "size: " + std::to_string(event.data.size()) + ")"; +        } + +        std::string operator()(const TLSDataForApplication& event) const { +            return std::string("TLSDataForApplication(") + "size: " + std::to_string(event.data.size()) + ")"; +        } + +        std::string operator()(const TLSFault&) const { +            return "TLSFault()"; +        } + +        std::string operator()(const TLSConnected& event) const { +            std::string certificates; +            for (auto cert : event.chain) { +                certificates += "\t" + cert->getSubjectName() + "\n"; +            } +            return std::string("TLSConnected()") + "\n" + certificates; +        } + +        std::string operator()(const TLSServerNameRequested& event) const { +            return std::string("TLSServerNameRequested(") + "name: " + event.name + ")"; +        } +}; + +class TLSClientServerEventHistory { +    public: +        TLSClientServerEventHistory(TLSContext* client, TLSContext* server) { +            connectContext(std::string("client"), client); +            connectContext(std::string("server"), server); +        } + +        __attribute__((unused)) +        void print() { +            auto count = 0; +            std::cout << "\n"; +            for (auto event : events) { +                if (event.first == "server") { +                    std::cout << std::string(80, ' '); +                } +                std::cout << count << ". "; +                std::cout << event.first << " : " << boost::apply_visitor(TLSEventToStringVisitor(), event.second) << std::endl; +                count++; +            } +        } + +        template<class TLSEventType> +        boost::optional<TLSEventType> getEvent(const std::string& peer, size_t number = 0) { +            for (const auto& pair : events) { +                if (pair.first == peer) { +                    if (pair.second.type() == typeid(TLSEventType)) { +                        if (number == 0) { +                            return boost::optional<TLSEventType>(boost::get<TLSEventType>(pair.second)); +                        } +                        else { +                            number--; +                        } +                    } +                } +            } +            return {}; +        } + +    private: +        void connectContext(const std::string& name, TLSContext* context) { +            connections_.push_back(context->onDataForNetwork.connect([=](const SafeByteArray& data) { +                events.push_back(std::pair<std::string, TLSEvent>(name, TLSDataForNetwork{data})); +            })); +            connections_.push_back(context->onDataForApplication.connect([=](const SafeByteArray& data) { +                events.push_back(std::pair<std::string, TLSEvent>(name, TLSDataForApplication{data})); +            })); +            connections_.push_back(context->onError.connect([=](std::shared_ptr<Swift::TLSError> error) { +                events.push_back(std::pair<std::string, TLSEvent>(name, TLSFault{error})); +            })); +            connections_.push_back(context->onConnected.connect([=](){ +                events.push_back(std::pair<std::string, TLSEvent>(name, TLSConnected{context->getPeerCertificateChain()})); +            })); +        } + +    public: +        std::vector<std::pair<std::string, TLSEvent>> events; + +    private: +        std::vector<boost::signals2::connection> connections_; +}; + +} + +TEST(ClientServerTest, testInitAndFreeContext) { +    auto tlsClientContext = createTLSContext(TLSContext::Mode::Client); +    auto tlsServerContext = createTLSContext(TLSContext::Mode::Server); +} + +TEST(ClientServerTest, testServerSetPrivateKey) { +    auto tlsServerContext = createTLSContext(TLSContext::Mode::Server); +} + +TEST(ClientServerTest, testServerSetCertificateChain) { +    auto tlsServerContext = createTLSContext(TLSContext::Mode::Server); +} + +TEST(ClientServerTest, testClientServerBasicCommunicationWithCertificateMissing) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"])); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(true, serverContext->setPrivateKey(privateKey)); + +    serverContext->accept(); +    clientContext->connect(); + +    ASSERT_EQ("server", events.events[1].first); +    ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second)); +} + +TEST(ClientServerTest, testClientServerBasicCommunicationWithPrivateKeyMissing) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"])))); + +    serverContext->accept(); +    clientContext->connect(); + +    ASSERT_EQ("server", events.events[1].first); +    ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second)); +} + +TEST(ClientServerTest, testWrongPrivateKeyAfterCertificate) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["montague.example"])); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(false, serverContext->setPrivateKey(privateKey)); + +    serverContext->accept(); +    clientContext->connect(); +} + +TEST(ClientServerTest, testWrongCertificateAfterPrivateKey) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["montague.example"])); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(true, serverContext->setPrivateKey(privateKey)); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"])))); + +    serverContext->accept(); +    clientContext->connect(); + +    ASSERT_EQ("server", events.events[1].first); +    ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second)); +} + +TEST(ClientServerTest, testClientServerBasicCommunication) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"])); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(true, serverContext->setPrivateKey(privateKey)); + +    serverContext->accept(); +    clientContext->connect(); + +    clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client.")); +    serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server.")); + +    auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server"); +    ASSERT_EQ(true, firstMessageFromClient.is_initialized()); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data)); +    auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client"); +    ASSERT_EQ(true, firstMessageFromServer.is_initialized()); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data)); +} + +TEST(ClientServerTest, testClientServerBasicCommunicationEncryptedPrivateKeyRightPassword) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["montague.example"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(montagueEncryptedPEM), createSafeByteArray("test")); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(true, serverContext->setPrivateKey(privateKey)); + +    serverContext->accept(); +    clientContext->connect(); + +    clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client.")); +    serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server.")); + +    auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server"); +    ASSERT_EQ(true, firstMessageFromClient.is_initialized()); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data)); +    auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client"); +    ASSERT_EQ(true, firstMessageFromServer.is_initialized()); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data)); +} + +TEST(ClientServerTest, testClientServerBasicCommunicationWithChainedCert) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    ASSERT_TRUE(serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["casigned.example.com"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["casigned.example.com"])); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_TRUE(serverContext->setPrivateKey(privateKey)); + +    serverContext->accept(); +    clientContext->connect(); + +    clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client.")); +    serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server.")); + +    auto serverEvent = std::find_if(events.events.begin(), events.events.end(), [](std::pair<std::string, TLSEvent>& event){ +        return event.first == "server" && (event.second.type() == typeid(TLSDataForApplication)); +    }); +    ASSERT_NE(events.events.end(), serverEvent); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(boost::apply_visitor(TLSEventToSafeByteArrayVisitor(), serverEvent->second))); +    auto clientEvent = std::find_if(events.events.begin(), events.events.end(), [](std::pair<std::string, TLSEvent>& event){ +        return event.first == "client" && (event.second.type() == typeid(TLSDataForApplication)); +    }); +    ASSERT_NE(events.events.end(), clientEvent); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(boost::apply_visitor(TLSEventToSafeByteArrayVisitor(), clientEvent->second))); +} + +TEST(ClientServerTest, testSettingPrivateKeyWithWrongPassword) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["montague.example"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(montagueEncryptedPEM), createSafeByteArray("foo")); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(false, serverContext->setPrivateKey(privateKey)); +} + +TEST(ClientServerTest, testSettingPrivateKeyWithoutRequiredPassword) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["montague.example"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(montagueEncryptedPEM)); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(false, serverContext->setPrivateKey(privateKey)); +} + +TEST(ClientServerTest, testClientServerSNIRequestedHostAvailable) { +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    serverContext->onServerNameRequested.connect([&](const std::string& requestedName) { +        if (certificatePEM.find(requestedName) != certificatePEM.end() && privateKeyPEM.find(requestedName) != privateKeyPEM.end()) { +            auto certChain = tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM[requestedName])); +            ASSERT_EQ(true, serverContext->setCertificateChain(std::move(certChain))); + +            auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM[requestedName])); +            ASSERT_NE(nullptr, privateKey.get()); +            ASSERT_EQ(true, serverContext->setPrivateKey(privateKey)); +        } +    }); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"])); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(true, serverContext->setPrivateKey(privateKey)); + +    serverContext->accept(); +    clientContext->connect("montague.example"); + +    clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client.")); +    serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server.")); + +    auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server"); +    ASSERT_EQ(true, firstMessageFromClient.is_initialized()); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data)); +    auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client"); +    ASSERT_EQ(true, firstMessageFromServer.is_initialized()); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data)); + +    ASSERT_EQ("/CN=montague.example", events.getEvent<TLSConnected>("client")->chain[0]->getSubjectName()); +} + +TEST(ClientServerTest, testClientServerSNIRequestedHostUnavailable) { +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    serverContext->onServerNameRequested.connect([&](const std::string&) { +        serverContext->setAbortTLSHandshake(true); +    }); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"])); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(true, serverContext->setPrivateKey(privateKey)); + +    serverContext->accept(); +    clientContext->connect("montague.example"); + +    ASSERT_EQ("server", events.events[1].first); +    ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second)); + +    ASSERT_EQ("client", events.events[3].first); +    ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[3].second)); +} + +TEST(ClientServerTest, testClientServerEqualFinishedMessage) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"])); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(true, serverContext->setPrivateKey(privateKey)); + +    serverContext->accept(); +    clientContext->connect(); + +    ASSERT_EQ(serverContext->getPeerFinishMessage(), clientContext->getFinishMessage()); +    ASSERT_EQ(clientContext->getPeerFinishMessage(), serverContext->getFinishMessage()); +} + +TEST(ClientServerTest, testClientServerBasicCommunicationWith2048BitDHParams) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"])); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(true, serverContext->setPrivateKey(privateKey)); + +    ASSERT_EQ(true, serverContext->setDiffieHellmanParameters(tlsFactories->getTLSContextFactory()->convertDHParametersFromPEMToDER(dhParamsOpenSslDer2048))); + +    serverContext->accept(); +    clientContext->connect(); + +    clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client.")); +    serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server.")); + +    auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server"); +    ASSERT_EQ(true, firstMessageFromClient.is_initialized()); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data)); +    auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client"); +    ASSERT_EQ(true, firstMessageFromServer.is_initialized()); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data)); +} + +TEST(ClientServerTest, testClientServerBasicCommunicationWith1024BitDHParams) { +    auto clientContext = createTLSContext(TLSContext::Mode::Client); +    auto serverContext = createTLSContext(TLSContext::Mode::Server); + +    TLSClientServerEventHistory events(clientContext.get(), serverContext.get()); + +    ClientServerConnector connector(clientContext.get(), serverContext.get()); + +    auto tlsFactories = std::make_shared<PlatformTLSFactories>(); + +    ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"])))); + +    auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"])); +    ASSERT_NE(nullptr, privateKey.get()); +    ASSERT_EQ(true, serverContext->setPrivateKey(privateKey)); + +    ASSERT_EQ(true, serverContext->setDiffieHellmanParameters(tlsFactories->getTLSContextFactory()->convertDHParametersFromPEMToDER(dhParamsOpenSslDer1024))); + +    serverContext->accept(); +    clientContext->connect(); + +    clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client.")); +    serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server.")); + +    auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server"); +    ASSERT_EQ(true, firstMessageFromClient.is_initialized()); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data)); +    auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client"); +    ASSERT_EQ(true, firstMessageFromServer.is_initialized()); +    ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data)); +} diff --git a/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp b/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp index 30fe423..47f3db2 100644 --- a/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp +++ b/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp @@ -35,6 +35,8 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {          CPPUNIT_TEST(testCertificateVerifies_WithMatchingInternationalXmppAddr);          CPPUNIT_TEST(testCertificateVerifies_WithMatchingCNWithoutSAN);          CPPUNIT_TEST(testCertificateVerifies_WithMatchingCNWithSAN); +        CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithServerExpected); +        CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithClientUnexpected);          CPPUNIT_TEST_SUITE_END();      public: @@ -58,6 +60,14 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {              CPPUNIT_ASSERT(testling.certificateVerifies(certificate));          } +        void testCertificateVerifies_WithMatchingDNSNameMixedCase() { +            ServerIdentityVerifier testling(JID("foo@baR.com/baz"), idnConverter.get()); +            SimpleCertificate::ref certificate(new SimpleCertificate()); +            certificate->addDNSName("Bar.com"); + +            CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); +        } +          void testCertificateVerifies_WithSecondMatchingDNSName() {              ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());              SimpleCertificate::ref certificate(new SimpleCertificate()); @@ -131,6 +141,24 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {              CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));          } +        void testCertificateVerifies_WithMatchingSRVNameWithServerExpected() { +            // Server-mode test which gets cert with "xmpp-server" SRV name +            ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get(), true); +            SimpleCertificate::ref certificate(new SimpleCertificate()); +            certificate->addSRVName("_xmpp-server.bar.com"); + +            CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); +        } + +        void testCertificateVerifies_WithMatchingSRVNameWithClientUnexpected() { +            // Server-mode test which gets cert with "xmpp-client" SRV name +            ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get(), true); +            SimpleCertificate::ref certificate(new SimpleCertificate()); +            certificate->addSRVName("_xmpp-client.bar.com"); + +            CPPUNIT_ASSERT(!testling.certificateVerifies(certificate)); +        } +          void testCertificateVerifies_WithMatchingXmppAddr() {              ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());              SimpleCertificate::ref certificate(new SimpleCertificate()); @@ -139,6 +167,14 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {              CPPUNIT_ASSERT(testling.certificateVerifies(certificate));          } +        void testCertificateVerifies_WithMatchingXmppAddrMixedCase() { +            ServerIdentityVerifier testling(JID("foo@baR.com/baz"), idnConverter.get()); +            SimpleCertificate::ref certificate(new SimpleCertificate()); +            certificate->addXMPPAddress("bAr.com"); + +            CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); +        } +          void testCertificateVerifies_WithMatchingXmppAddrWithWildcard() {              ServerIdentityVerifier testling(JID("foo@im.bar.com/baz"), idnConverter.get());              SimpleCertificate::ref certificate(new SimpleCertificate()); @@ -147,6 +183,14 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {              CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));          } +        void testCertificateVerifies_WithMatchingXmppAddrWithWildcardMixedCase() { +            ServerIdentityVerifier testling(JID("foo@im.bAr.com/baz"), idnConverter.get()); +            SimpleCertificate::ref certificate(new SimpleCertificate()); +            certificate->addXMPPAddress("*.baR.com"); + +            CPPUNIT_ASSERT(!testling.certificateVerifies(certificate)); +        } +          void testCertificateVerifies_WithMatchingInternationalXmppAddr() {              ServerIdentityVerifier testling(JID("foo@tron\xc3\xa7.com/baz"), idnConverter.get());              SimpleCertificate::ref certificate(new SimpleCertificate()); @@ -155,6 +199,14 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {              CPPUNIT_ASSERT(testling.certificateVerifies(certificate));          } +        void testCertificateVerifies_WithMatchingInternationalXmppAddrMixedCase() { +            ServerIdentityVerifier testling(JID("foo@tRon\xc3\xa7.com/baz"), idnConverter.get()); +            SimpleCertificate::ref certificate(new SimpleCertificate()); +            certificate->addXMPPAddress("trOn\xc3\xa7.com"); + +            CPPUNIT_ASSERT(testling.certificateVerifies(certificate)); +        } +          void testCertificateVerifies_WithMatchingCNWithoutSAN() {              ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());              SimpleCertificate::ref certificate(new SimpleCertificate());  | 
 Swift