diff options
Diffstat (limited to 'Swiften/TLS/OpenSSL')
-rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp | 156 | ||||
-rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLCertificate.h | 121 | ||||
-rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp | 56 | ||||
-rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h | 20 | ||||
-rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLContext.cpp | 1045 | ||||
-rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLContext.h | 101 | ||||
-rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp | 55 | ||||
-rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLContextFactory.h | 27 |
8 files changed, 1133 insertions, 448 deletions
diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp index d654787..66b650d 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/TLS/OpenSSL/OpenSSLCertificate.h> @@ -19,92 +19,100 @@ namespace Swift { -OpenSSLCertificate::OpenSSLCertificate(boost::shared_ptr<X509> cert) : cert(cert) { - parse(); +OpenSSLCertificate::OpenSSLCertificate(std::shared_ptr<X509> cert) : cert(cert) { + parse(); } OpenSSLCertificate::OpenSSLCertificate(const ByteArray& der) { #if OPENSSL_VERSION_NUMBER <= 0x009070cfL - unsigned char* p = const_cast<unsigned char*>(vecptr(der)); + unsigned char* p = const_cast<unsigned char*>(vecptr(der)); #else - const unsigned char* p = vecptr(der); + const unsigned char* p = vecptr(der); +#endif + cert = std::shared_ptr<X509>(d2i_X509(nullptr, &p, der.size()), X509_free); + if (!cert) { +// 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 - cert = boost::shared_ptr<X509>(d2i_X509(NULL, &p, der.size()), X509_free); - if (!cert) { - SWIFT_LOG(warning) << "Error creating certificate from DER data" << std::endl; - } - parse(); } ByteArray OpenSSLCertificate::toDER() const { - ByteArray result; - if (!cert) { - return result; - } - result.resize(i2d_X509(cert.get(), NULL)); - unsigned char* p = vecptr(result); - i2d_X509(cert.get(), &p); - return result; + ByteArray result; + if (!cert) { + return result; + } + result.resize(i2d_X509(cert.get(), nullptr)); + unsigned char* p = vecptr(result); + i2d_X509(cert.get(), &p); + return result; } void OpenSSLCertificate::parse() { - if (!cert) { - return; - } - // Subject name - X509_NAME* subjectName = X509_get_subject_name(cert.get()); - if (subjectName) { - // Subject name - ByteArray subjectNameData; - subjectNameData.resize(256); - X509_NAME_oneline(X509_get_subject_name(cert.get()), reinterpret_cast<char*>(vecptr(subjectNameData)), static_cast<unsigned int>(subjectNameData.size())); - this->subjectName = byteArrayToString(subjectNameData); + if (!cert) { + return; + } + // Subject name + X509_NAME* subjectName = X509_get_subject_name(cert.get()); + if (subjectName) { + // Subject name + ByteArray subjectNameData; + subjectNameData.resize(256); + X509_NAME_oneline(X509_get_subject_name(cert.get()), reinterpret_cast<char*>(vecptr(subjectNameData)), static_cast<unsigned int>(subjectNameData.size())); + this->subjectName = byteArrayToString(subjectNameData); - // Common name - int cnLoc = X509_NAME_get_index_by_NID(subjectName, NID_commonName, -1); - while (cnLoc != -1) { - X509_NAME_ENTRY* cnEntry = X509_NAME_get_entry(subjectName, cnLoc); - ASN1_STRING* cnData = X509_NAME_ENTRY_get_data(cnEntry); - commonNames.push_back(byteArrayToString(createByteArray(reinterpret_cast<const char*>(cnData->data), cnData->length))); - cnLoc = X509_NAME_get_index_by_NID(subjectName, NID_commonName, cnLoc); - } - } + // Common name + int cnLoc = X509_NAME_get_index_by_NID(subjectName, NID_commonName, -1); + while (cnLoc != -1) { + X509_NAME_ENTRY* cnEntry = X509_NAME_get_entry(subjectName, cnLoc); + ASN1_STRING* cnData = X509_NAME_ENTRY_get_data(cnEntry); + commonNames.push_back(byteArrayToString(createByteArray(reinterpret_cast<const char*>(cnData->data), cnData->length))); + cnLoc = X509_NAME_get_index_by_NID(subjectName, NID_commonName, cnLoc); + } + } - // subjectAltNames - int subjectAltNameLoc = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1); - if(subjectAltNameLoc != -1) { - X509_EXTENSION* extension = X509_get_ext(cert.get(), subjectAltNameLoc); - boost::shared_ptr<GENERAL_NAMES> generalNames(reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(extension)), GENERAL_NAMES_free); - boost::shared_ptr<ASN1_OBJECT> xmppAddrObject(OBJ_txt2obj(ID_ON_XMPPADDR_OID, 1), ASN1_OBJECT_free); - boost::shared_ptr<ASN1_OBJECT> dnsSRVObject(OBJ_txt2obj(ID_ON_DNSSRV_OID, 1), ASN1_OBJECT_free); - for (int i = 0; i < sk_GENERAL_NAME_num(generalNames.get()); ++i) { - GENERAL_NAME* generalName = sk_GENERAL_NAME_value(generalNames.get(), i); - if (generalName->type == GEN_OTHERNAME) { - OTHERNAME* otherName = generalName->d.otherName; - if (OBJ_cmp(otherName->type_id, xmppAddrObject.get()) == 0) { - // XmppAddr - if (otherName->value->type != V_ASN1_UTF8STRING) { - continue; - } - ASN1_UTF8STRING* xmppAddrValue = otherName->value->value.utf8string; - addXMPPAddress(byteArrayToString(createByteArray(reinterpret_cast<const char*>(ASN1_STRING_data(xmppAddrValue)), ASN1_STRING_length(xmppAddrValue)))); - } - else if (OBJ_cmp(otherName->type_id, dnsSRVObject.get()) == 0) { - // SRVName - if (otherName->value->type != V_ASN1_IA5STRING) { - continue; - } - ASN1_IA5STRING* srvNameValue = otherName->value->value.ia5string; - addSRVName(byteArrayToString(createByteArray(reinterpret_cast<const char*>(ASN1_STRING_data(srvNameValue)), ASN1_STRING_length(srvNameValue)))); - } - } - else if (generalName->type == GEN_DNS) { - // DNSName - addDNSName(byteArrayToString(createByteArray(ASN1_STRING_data(generalName->d.dNSName), ASN1_STRING_length(generalName->d.dNSName)))); - } - } - } + // subjectAltNames + int subjectAltNameLoc = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1); + if(subjectAltNameLoc != -1) { + X509_EXTENSION* extension = X509_get_ext(cert.get(), subjectAltNameLoc); + std::shared_ptr<GENERAL_NAMES> generalNames(reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(extension)), GENERAL_NAMES_free); + std::shared_ptr<ASN1_OBJECT> xmppAddrObject(OBJ_txt2obj(ID_ON_XMPPADDR_OID, 1), ASN1_OBJECT_free); + std::shared_ptr<ASN1_OBJECT> dnsSRVObject(OBJ_txt2obj(ID_ON_DNSSRV_OID, 1), ASN1_OBJECT_free); + for (int i = 0; i < sk_GENERAL_NAME_num(generalNames.get()); ++i) { + GENERAL_NAME* generalName = sk_GENERAL_NAME_value(generalNames.get(), i); + if (generalName->type == GEN_OTHERNAME) { + OTHERNAME* otherName = generalName->d.otherName; + if (OBJ_cmp(otherName->type_id, xmppAddrObject.get()) == 0) { + // XmppAddr + if (otherName->value->type != V_ASN1_UTF8STRING) { + continue; + } + ASN1_UTF8STRING* xmppAddrValue = otherName->value->value.utf8string; + addXMPPAddress(byteArrayToString(createByteArray(reinterpret_cast<const char*>(ASN1_STRING_data(xmppAddrValue)), ASN1_STRING_length(xmppAddrValue)))); + } + else if (OBJ_cmp(otherName->type_id, dnsSRVObject.get()) == 0) { + // SRVName + if (otherName->value->type != V_ASN1_IA5STRING) { + continue; + } + ASN1_IA5STRING* srvNameValue = otherName->value->value.ia5string; + addSRVName(byteArrayToString(createByteArray(reinterpret_cast<const char*>(ASN1_STRING_data(srvNameValue)), ASN1_STRING_length(srvNameValue)))); + } + } + else if (generalName->type == GEN_DNS) { + // DNSName + addDNSName(byteArrayToString(createByteArray(ASN1_STRING_data(generalName->d.dNSName), ASN1_STRING_length(generalName->d.dNSName)))); + } + } + } } } diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificate.h b/Swiften/TLS/OpenSSL/OpenSSLCertificate.h index 897b432..64da82a 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLCertificate.h +++ b/Swiften/TLS/OpenSSL/OpenSSLCertificate.h @@ -1,70 +1,73 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <boost/shared_ptr.hpp> +#include <memory> +#include <string> + #include <openssl/ssl.h> -#include <string> #include <Swiften/TLS/Certificate.h> namespace Swift { - class OpenSSLCertificate : public Certificate { - public: - OpenSSLCertificate(boost::shared_ptr<X509>); - OpenSSLCertificate(const ByteArray& der); - - std::string getSubjectName() const { - return subjectName; - } - - std::vector<std::string> getCommonNames() const { - return commonNames; - } - - std::vector<std::string> getSRVNames() const { - return srvNames; - } - - std::vector<std::string> getDNSNames() const { - return dnsNames; - } - - std::vector<std::string> getXMPPAddresses() const { - return xmppAddresses; - } - - ByteArray toDER() const; - - boost::shared_ptr<X509> getInternalX509() const { - return cert; - } - - private: - void parse(); - - void addSRVName(const std::string& name) { - srvNames.push_back(name); - } - - void addDNSName(const std::string& name) { - dnsNames.push_back(name); - } - - void addXMPPAddress(const std::string& addr) { - xmppAddresses.push_back(addr); - } - - private: - boost::shared_ptr<X509> cert; - std::string subjectName; - std::vector<std::string> commonNames; - std::vector<std::string> dnsNames; - std::vector<std::string> xmppAddresses; - std::vector<std::string> srvNames; - }; + class OpenSSLCertificate : public Certificate { + public: + OpenSSLCertificate(std::shared_ptr<X509>); + OpenSSLCertificate(const ByteArray& der); + + std::string getSubjectName() const { + return subjectName; + } + + std::vector<std::string> getCommonNames() const { + return commonNames; + } + + std::vector<std::string> getSRVNames() const { + return srvNames; + } + + std::vector<std::string> getDNSNames() const { + return dnsNames; + } + + std::vector<std::string> getXMPPAddresses() const { + return xmppAddresses; + } + + ByteArray toDER() const; + + std::shared_ptr<X509> getInternalX509() const { + return cert; + } + + void incrementReferenceCount() const; + + private: + void parse(); + + void addSRVName(const std::string& name) { + srvNames.push_back(name); + } + + void addDNSName(const std::string& name) { + dnsNames.push_back(name); + } + + void addXMPPAddress(const std::string& addr) { + xmppAddresses.push_back(addr); + } + + private: + std::shared_ptr<X509> cert; + std::string subjectName; + std::vector<std::string> commonNames; + std::vector<std::string> dnsNames; + std::vector<std::string> xmppAddresses; + std::vector<std::string> srvNames; + }; } 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 52f134c..a6974c8 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h +++ b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -10,10 +10,12 @@ #include <Swiften/TLS/OpenSSL/OpenSSLCertificate.h> namespace Swift { - class OpenSSLCertificateFactory : public CertificateFactory { - public: - virtual Certificate::ref createCertificateFromDER(const ByteArray& der) { - return Certificate::ref(new OpenSSLCertificate(der)); - } - }; + class OpenSSLCertificateFactory : public CertificateFactory { + public: + 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 77f780f..86b0504 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp @@ -1,8 +1,9 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * 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 <boost/smart_ptr/make_shared.hpp> #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,300 +42,834 @@ 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); + sk_X509_free(stack); } -OpenSSLContext::OpenSSLContext() : state_(Start), context_(0), handle_(0), readBIO_(0), writeBIO_(0) { - ensureLibraryInitialized(); - context_ = SSL_CTX_new(TLSv1_client_method()); +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_ = 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) + // TODO: cache CRL downloads for configurable time period - // TODO: implement CRL checking - // TODO: download CRL (HTTP transport) - // TODO: cache CRL downloads for configurable time period + // TODO: implement OCSP support + // TODO: handle OCSP stapling see https://www.rfc-editor.org/rfc/rfc4366.txt - // 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_load_verify_locations(context_, NULL, "/etc/ssl/certs"); -#elif defined(SWIFTEN_PLATFORM_MACOSX) - // 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()); - } - } - CFRelease(anchorCertificates); - } + 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_.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); + } #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(); - - // Disable compression - /* - STACK_OF(SSL_COMP)* compressionMethods = SSL_COMP_get_compression_methods(); - sk_SSL_COMP_zero(compressionMethods);*/ - - isLibraryInitialized = true; - } + 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); + + 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; + } + + 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_); - // Ownership of BIOs is ransferred - readBIO_ = BIO_new(BIO_s_mem()); - writeBIO_ = BIO_new(BIO_s_mem()); - SSL_set_bio(handle_, readBIO_, writeBIO_); - - state_ = Connecting; - doConnect(); + 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; + } + + 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_ = 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); - switch (error) { - case SSL_ERROR_NONE: { - state_ = Connected; - //std::cout << x->name << std::endl; - //const char* comp = SSL_get_current_compression(handle_); - //std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl; - onConnected(); - break; - } - case SSL_ERROR_WANT_READ: - sendPendingDataToNetwork(); - break; - default: - state_ = Error; - onError(boost::make_shared<TLSError>()); - } + int connectResult = SSL_connect(handle_.get()); + int error = SSL_get_error(handle_.get(), connectResult); + 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 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_ = 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() { - int size = BIO_pending(writeBIO_); - if (size > 0) { - SafeByteArray data; - data.resize(size); - BIO_read(writeBIO_, vecptr(data), size); - onDataForNetwork(data); - } + int size = BIO_pending(writeBIO_); + if (size > 0) { + SafeByteArray data; + data.resize(size); + BIO_read(writeBIO_, vecptr(data), size); + onDataForNetwork(data); + } } void OpenSSLContext::handleDataFromNetwork(const SafeByteArray& data) { - BIO_write(readBIO_, vecptr(data), data.size()); - switch (state_) { - case Connecting: - doConnect(); - break; - case Connected: - sendPendingDataToApplication(); - break; - case Start: assert(false); break; - case Error: /*assert(false);*/ break; - } + BIO_write(readBIO_, vecptr(data), data.size()); + switch (state_) { + case State::Accepting: + doAccept(); + break; + case State::Connecting: + doConnect(); + break; + case State::Connected: + sendPendingDataToApplication(); + 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(); - } - else { - state_ = Error; - onError(boost::make_shared<TLSError>()); - } + 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_ = 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()); - while (ret > 0) { - data.resize(ret); - onDataForApplication(data); - data.resize(SSL_READ_BUFFERSIZE); - ret = SSL_read(handle_, vecptr(data), data.size()); - } - if (ret < 0 && SSL_get_error(handle_, ret) != SSL_ERROR_WANT_READ) { - state_ = Error; - onError(boost::make_shared<TLSError>()); - } + SafeByteArray data; + data.resize(SSL_READ_BUFFERSIZE); + 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_.get(), vecptr(data), data.size()); + } + 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) { - boost::shared_ptr<PKCS12Certificate> pkcs12Certificate = boost::dynamic_pointer_cast<PKCS12Certificate>(certificate); - if (!pkcs12Certificate || pkcs12Certificate->isNull()) { - return false; - } - - // Create a PKCS12 structure - BIO* bio = BIO_new(BIO_s_mem()); - BIO_write(bio, vecptr(pkcs12Certificate->getData()), pkcs12Certificate->getData().size()); - boost::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, NULL), PKCS12_free); - BIO_free(bio); - if (!pkcs12) { - return false; - } - - // Parse PKCS12 - X509 *certPtr = 0; - EVP_PKEY* privateKeyPtr = 0; - STACK_OF(X509)* caCertsPtr = 0; - int result = PKCS12_parse(pkcs12.get(), reinterpret_cast<const char*>(vecptr(pkcs12Certificate->getPassword())), &privateKeyPtr, &certPtr, &caCertsPtr); - if (result != 1) { - return false; - } - boost::shared_ptr<X509> cert(certPtr, X509_free); - boost::shared_ptr<EVP_PKEY> privateKey(privateKeyPtr, EVP_PKEY_free); - boost::shared_ptr<STACK_OF(X509)> caCerts(caCertsPtr, freeX509Stack); - - // Use the key & certificates - if (SSL_CTX_use_certificate(context_, cert.get()) != 1) { - return false; - } - if (SSL_CTX_use_PrivateKey(context_, 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)); - } - return true; + std::shared_ptr<PKCS12Certificate> pkcs12Certificate = std::dynamic_pointer_cast<PKCS12Certificate>(certificate); + if (!pkcs12Certificate || pkcs12Certificate->isNull()) { + return false; + } + + // 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, nullptr), PKCS12_free); + BIO_free(bio); + if (!pkcs12) { + return false; + } + + // Parse PKCS12 + 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); + if (result != 1) { + return false; + } + std::shared_ptr<X509> cert(certPtr, X509_free); + std::shared_ptr<EVP_PKEY> privateKey(privateKeyPtr, EVP_PKEY_free); + std::shared_ptr<STACK_OF(X509)> caCerts(caCertsPtr, freeX509Stack); + + // Use the key & certificates + if (SSL_CTX_use_certificate(context_.get(), cert.get()) != 1) { + return false; + } + 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_.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_); - for (int i = 0; i < sk_X509_num(chain); ++i) { - boost::shared_ptr<X509> x509Cert(X509_dup(sk_X509_value(chain, i)), X509_free); - - Certificate::ref cert = boost::make_shared<OpenSSLCertificate>(x509Cert); - result.push_back(cert); - } - return result; + std::vector<Certificate::ref> result; + + // 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; } -boost::shared_ptr<CertificateVerificationError> OpenSSLContext::getPeerCertificateVerificationError() const { - int verifyResult = SSL_get_verify_result(handle_); - if (verifyResult != X509_V_OK) { - return boost::make_shared<CertificateVerificationError>(getVerificationErrorTypeForResult(verifyResult)); - } - else { - return boost::shared_ptr<CertificateVerificationError>(); - } +std::shared_ptr<CertificateVerificationError> OpenSSLContext::getPeerCertificateVerificationError() const { + int verifyResult = SSL_get_verify_result(handle_.get()); + if (verifyResult != X509_V_OK) { + return std::make_shared<CertificateVerificationError>(getVerificationErrorTypeForResult(verifyResult)); + } + else { + return std::shared_ptr<CertificateVerificationError>(); + } } ByteArray OpenSSLContext::getFinishMessage() const { - ByteArray data; - data.resize(MAX_FINISHED_SIZE); - size_t size = SSL_get_finished(handle_, vecptr(data), data.size()); - data.resize(size); - return data; + ByteArray data; + data.resize(MAX_FINISHED_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) { - case X509_V_ERR_CERT_NOT_YET_VALID: - case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: - return CertificateVerificationError::NotYetValid; - - case X509_V_ERR_CERT_HAS_EXPIRED: - case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: - return CertificateVerificationError::Expired; - - case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: - return CertificateVerificationError::SelfSigned; - - case X509_V_ERR_CERT_UNTRUSTED: - return CertificateVerificationError::Untrusted; - - case X509_V_ERR_CERT_REJECTED: - return CertificateVerificationError::Rejected; - - case X509_V_ERR_INVALID_PURPOSE: - return CertificateVerificationError::InvalidPurpose; - - case X509_V_ERR_PATH_LENGTH_EXCEEDED: - return CertificateVerificationError::PathLengthExceeded; - - case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: - case X509_V_ERR_CERT_SIGNATURE_FAILURE: - case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: - return CertificateVerificationError::InvalidSignature; - - case X509_V_ERR_INVALID_CA: - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: - case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: - return CertificateVerificationError::InvalidCA; - - case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: - case X509_V_ERR_AKID_SKID_MISMATCH: - case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: - case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: - return CertificateVerificationError::UnknownError; - - // Unused / should not happen - case X509_V_ERR_CERT_REVOKED: - case X509_V_ERR_OUT_OF_MEM: - case X509_V_ERR_UNABLE_TO_GET_CRL: - case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: - case X509_V_ERR_CRL_SIGNATURE_FAILURE: - case X509_V_ERR_CRL_NOT_YET_VALID: - case X509_V_ERR_CRL_HAS_EXPIRED: - case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: - case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: - case X509_V_ERR_CERT_CHAIN_TOO_LONG: - case X509_V_ERR_APPLICATION_VERIFICATION: - default: - return CertificateVerificationError::UnknownError; - } + assert(result != 0); + switch (result) { + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + return CertificateVerificationError::NotYetValid; + + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + return CertificateVerificationError::Expired; + + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + return CertificateVerificationError::SelfSigned; + + case X509_V_ERR_CERT_UNTRUSTED: + return CertificateVerificationError::Untrusted; + + case X509_V_ERR_CERT_REJECTED: + return CertificateVerificationError::Rejected; + + case X509_V_ERR_INVALID_PURPOSE: + return CertificateVerificationError::InvalidPurpose; + + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + return CertificateVerificationError::PathLengthExceeded; + + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + return CertificateVerificationError::InvalidSignature; + + case X509_V_ERR_INVALID_CA: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + return CertificateVerificationError::InvalidCA; + + case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: + case X509_V_ERR_AKID_SKID_MISMATCH: + case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: + case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: + return CertificateVerificationError::UnknownError; + + // Unused / should not happen + case X509_V_ERR_CERT_REVOKED: + case X509_V_ERR_OUT_OF_MEM: + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + case X509_V_ERR_CRL_NOT_YET_VALID: + case X509_V_ERR_CRL_HAS_EXPIRED: + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + case X509_V_ERR_APPLICATION_VERIFICATION: + default: + return CertificateVerificationError::UnknownError; + } } } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h index d4327ca..8eb5758 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.h +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h @@ -1,53 +1,92 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <openssl/ssl.h> -#include <Swiften/Base/boost_bsignals.h> +#include <memory> + #include <boost/noncopyable.hpp> +#include <boost/signals2.hpp> + +#include <openssl/ssl.h> -#include <Swiften/TLS/TLSContext.h> #include <Swiften/Base/ByteArray.h> #include <Swiften/TLS/CertificateWithKey.h> +#include <Swiften/TLS/TLSContext.h> +#include <Swiften/TLS/TLSOptions.h> + +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(const TLSOptions& options, Mode mode); + virtual ~OpenSSLContext() override final; - class OpenSSLContext : public TLSContext, boost::noncopyable { - public: - OpenSSLContext(); - ~OpenSSLContext(); + void accept() override final; + void connect() override final; + void connect(const std::string& requestHostname) override final; - void connect(); - bool setClientCertificate(CertificateWithKey::ref cert); + 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 handleDataFromNetwork(const SafeByteArray&); - void handleDataFromApplication(const SafeByteArray&); + void handleDataFromNetwork(const SafeByteArray&) override final; + void handleDataFromApplication(const SafeByteArray&) override final; - std::vector<Certificate::ref> getPeerCertificateChain() const; - boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; + std::vector<Certificate::ref> getPeerCertificateChain() const override final; + std::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const override final; - virtual ByteArray getFinishMessage() const; + virtual ByteArray getFinishMessage() const override final; + virtual ByteArray getPeerFinishMessage() const override final; - private: - static void ensureLibraryInitialized(); + void setX509StoreContext(X509_STORE_CTX *ptr) { x509_store_ctx = ptr; } + std::function<int (const TLSContext *)> getVerifyCertCallback() { return verifyCertCallback; } - static CertificateVerificationError::Type getVerificationErrorTypeForResult(int); + private: + bool configure(const TLSOptions& options); + static void ensureLibraryInitialized(); + static int handleServerNameCallback(SSL *ssl, int *ad, void *arg); + static CertificateVerificationError::Type getVerificationErrorTypeForResult(int); - void doConnect(); - void sendPendingDataToNetwork(); - void sendPendingDataToApplication(); + void initAndSetBIOs(); + void doAccept(); + void doConnect(); + void sendPendingDataToNetwork(); + void sendPendingDataToApplication(); - private: - enum State { Start, Connecting, Connected, Error }; + private: + enum class State { Start, Accepting, Connecting, Connected, Error }; - State state_; - SSL_CTX* context_; - SSL* handle_; - BIO* readBIO_; - BIO* writeBIO_; - }; + const Mode mode_; + State state_; + 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 671cba7..e332ca8 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp @@ -1,28 +1,61 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/TLS/OpenSSL/OpenSSLContextFactory.h> -#include <Swiften/TLS/OpenSSL/OpenSSLContext.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; + return true; } -TLSContext* OpenSSLContextFactory::createTLSContext() { - 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) { - assert(false); - SWIFT_LOG(warning) << "CRL Checking not supported for OpenSSL" << std::endl; - } + if (check) { + 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"; + } } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h index 43ab960..834e479 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h +++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h @@ -1,22 +1,25 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <Swiften/TLS/TLSContextFactory.h> +#include <memory> -#include <cassert> +#include <Swiften/TLS/TLSContextFactory.h> namespace Swift { - class OpenSSLContextFactory : public TLSContextFactory { - public: - bool canCreate() const; - virtual TLSContext* createTLSContext(); + class OpenSSLContextFactory : public TLSContextFactory { + public: + 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); - }; + // Not supported + virtual void setCheckCertificateRevocation(bool b) override final; + virtual void setDisconnectOnCardRemoval(bool b) override final; + }; } |