diff options
author | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
---|---|---|
committer | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
commit | aa4d426b4d3527d7e166df1a05058c9a4a0f6683 (patch) | |
tree | 4ff17ce8b89a2321b9d0ed4bcfc37c447bcb6820 /vmime-master/src/vmime/net/tls/openssl | |
download | smtps-and-pop3s-console-program-master.tar.gz smtps-and-pop3s-console-program-master.zip |
Diffstat (limited to 'vmime-master/src/vmime/net/tls/openssl')
8 files changed, 1598 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp b/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp new file mode 100644 index 0000000..c7b1013 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp @@ -0,0 +1,169 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/net/tls/openssl/OpenSSLInitializer.hpp" + +#include "vmime/platform.hpp" + +#include <openssl/opensslv.h> + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +# define OPENSSL_API_COMPAT 0x10100000L +#endif + +#include <openssl/ssl.h> +#include <openssl/rand.h> +#include <openssl/crypto.h> +#include <openssl/err.h> + +#if OPENSSL_VERSION_NUMBER >= 0x0907000L +# include <openssl/conf.h> +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +# include "vmime/utility/sync/autoLock.hpp" +# include "vmime/utility/sync/criticalSection.hpp" +#endif + + +// OpenSSL locking callbacks for multithreading support (< v1.1 only) +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +namespace { + +vmime::shared_ptr <vmime::utility::sync::criticalSection >* g_openSSLMutexes = NULL; + +extern "C" void VMime_OpenSSLCallback_lock(int mode, int n, const char* /* file */, int /* line */) { + + if (mode & CRYPTO_LOCK) { + g_openSSLMutexes[n]->lock(); + } else { + g_openSSLMutexes[n]->unlock(); + } +} + +extern "C" unsigned long VMime_OpenSSLCallback_id() { + + return vmime::platform::getHandler()->getThreadId(); +} + +} + +#endif + + +namespace vmime { +namespace net { +namespace tls { + + +OpenSSLInitializer::autoInitializer::autoInitializer() { + + // The construction of this unique 'oneTimeInitializer' object will be triggered + // by the 'autoInitializer' objects from the other translation units + static OpenSSLInitializer::oneTimeInitializer oneTimeInitializer; +} + + +OpenSSLInitializer::autoInitializer::~autoInitializer() { + +} + + +OpenSSLInitializer::oneTimeInitializer::oneTimeInitializer() { + + initialize(); +} + + +OpenSSLInitializer::oneTimeInitializer::~oneTimeInitializer() { + + uninitialize(); +} + + +// static +void OpenSSLInitializer::initialize() { + +#if OPENSSL_VERSION_NUMBER >= 0x0907000L && OPENSSL_VERSION_NUMBER < 0x10100000L + OPENSSL_config(NULL); +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + + int numMutexes = CRYPTO_num_locks(); + g_openSSLMutexes = new shared_ptr <vmime::utility::sync::criticalSection>[numMutexes]; + + for (int i = 0 ; i < numMutexes ; ++i) { + g_openSSLMutexes[i] = vmime::platform::getHandler()->createCriticalSection(); + } + + CRYPTO_set_locking_callback(VMime_OpenSSLCallback_lock); + CRYPTO_set_id_callback(VMime_OpenSSLCallback_id); +#endif + + // Seed the RNG, in case /dev/urandom is not available. Explicitely calling + // RAND_seed() even though /dev/urandom is available is harmless. + enum { + SEEDSIZE = 256 + }; + + unsigned char seed[SEEDSIZE]; + vmime::platform::getHandler()->generateRandomBytes(seed, SEEDSIZE); + RAND_seed(seed, SEEDSIZE); +} + + +// static +void OpenSSLInitializer::uninitialize() { + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + EVP_cleanup(); + ERR_free_strings(); + + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + + delete [] g_openSSLMutexes; + g_openSSLMutexes = NULL; +#endif + +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + diff --git a/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.hpp b/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.hpp new file mode 100644 index 0000000..3b8496d --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/OpenSSLInitializer.hpp @@ -0,0 +1,90 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_NET_TLS_OPENSSL_OPENSSLINITIALIZER_HPP_INCLUDED +#define VMIME_NET_TLS_OPENSSL_OPENSSLINITIALIZER_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + +#include <vector> + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/utility/sync/criticalSection.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +/** Class responsible for setting up OpenSSL + */ +class OpenSSLInitializer { + +public: + + /** Automatically initialize OpenSSL + */ + struct autoInitializer { + + autoInitializer(); + ~autoInitializer(); + }; + +protected: + + struct oneTimeInitializer { + + oneTimeInitializer(); + ~oneTimeInitializer(); + }; + + + /** Initializes the OpenSSL lib + */ + static void initialize(); + + /** Shutdown the OpenSSL lib + */ + static void uninitialize(); +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_OPENSSL_OPENSSLINITIALIZER_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.cpp b/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.cpp new file mode 100644 index 0000000..ea22f1c --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.cpp @@ -0,0 +1,112 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/base.hpp" +#include "vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp" + +#include <openssl/ssl.h> +#include <openssl/err.h> + + +namespace vmime { +namespace net { +namespace tls { + + +TLSProperties::TLSProperties() + : m_data(make_shared <TLSProperties_OpenSSL>()) { + + setCipherSuite(CIPHERSUITE_DEFAULT); +} + + +TLSProperties::TLSProperties(const TLSProperties& props) + : object(), + m_data(make_shared <TLSProperties_OpenSSL>()) { + + *dynamicCast <TLSProperties_OpenSSL>(m_data) = *dynamicCast <TLSProperties_OpenSSL>(props.m_data); +} + + +void TLSProperties::setCipherSuite(const GenericCipherSuite cipherSuite) { + + switch (cipherSuite) { + + case CIPHERSUITE_HIGH: + + setCipherSuite("HIGH:!ADH:@STRENGTH"); + break; + + case CIPHERSUITE_MEDIUM: + + setCipherSuite("MEDIUM:!ADH:@STRENGTH"); + break; + + case CIPHERSUITE_LOW: + + setCipherSuite("LOW:!ADH:@STRENGTH"); + break; + + default: + case CIPHERSUITE_DEFAULT: + + setCipherSuite("DEFAULT:!ADH:@STRENGTH"); + break; + } +} + + +void TLSProperties::setCipherSuite(const string& cipherSuite) { + + dynamicCast <TLSProperties_OpenSSL>(m_data)->cipherSuite = cipherSuite; +} + + +const string TLSProperties::getCipherSuite() const { + + return dynamicCast <TLSProperties_OpenSSL>(m_data)->cipherSuite; +} + + + +TLSProperties_OpenSSL& TLSProperties_OpenSSL::operator=(const TLSProperties_OpenSSL& other) { + + cipherSuite = other.cipherSuite; + + return *this; +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp b/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp new file mode 100644 index 0000000..8304df2 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp @@ -0,0 +1,68 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_NET_TLS_TLSPROPERTIES_OPENSSL_HPP_INCLUDED +#define VMIME_NET_TLS_TLSPROPERTIES_OPENSSL_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/TLSProperties.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSProperties_OpenSSL : public object { + +public: + + TLSProperties_OpenSSL& operator=(const TLSProperties_OpenSSL& other); + + + string cipherSuite; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSPROPERTIES_OPENSSL_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.cpp b/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.cpp new file mode 100644 index 0000000..019341c --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.cpp @@ -0,0 +1,147 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/net/tls/openssl/TLSSession_OpenSSL.hpp" +#include "vmime/net/tls/openssl/TLSProperties_OpenSSL.hpp" +#include "vmime/net/tls/openssl/OpenSSLInitializer.hpp" + +#include "vmime/security/cert/certificateException.hpp" + +#include <openssl/ssl.h> +#include <openssl/err.h> + + +namespace vmime { +namespace net { +namespace tls { + + +static OpenSSLInitializer::autoInitializer openSSLInitializer; + + +// static +shared_ptr <TLSSession> TLSSession::create( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props +) { + + return make_shared <TLSSession_OpenSSL>(cv, props); +} + + +TLSSession_OpenSSL::TLSSession_OpenSSL( + const shared_ptr <vmime::security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props +) + : m_sslctx(0), + m_certVerifier(cv), + m_props(props) { + + m_sslctx = SSL_CTX_new(SSLv23_client_method()); + SSL_CTX_set_options(m_sslctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); + SSL_CTX_set_mode(m_sslctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_cipher_list(m_sslctx, m_props->getCipherSuite().c_str()); + SSL_CTX_set_session_cache_mode(m_sslctx, SSL_SESS_CACHE_OFF); +} + + +TLSSession_OpenSSL::TLSSession_OpenSSL(const TLSSession_OpenSSL&) + : TLSSession() { + + // Not used +} + + +TLSSession_OpenSSL::~TLSSession_OpenSSL() { + + SSL_CTX_free(m_sslctx); +} + + +shared_ptr <TLSSocket> TLSSession_OpenSSL::getSocket(const shared_ptr <socket>& sok) { + + return TLSSocket::wrap(dynamicCast <TLSSession>(shared_from_this()), sok); +} + + +shared_ptr <security::cert::certificateVerifier> TLSSession_OpenSSL::getCertificateVerifier() { + + return m_certVerifier; +} + + +void TLSSession_OpenSSL::usePrivateKeyFile(const vmime::string& keyfile) { + + ERR_clear_error(); + + if (SSL_CTX_use_PrivateKey_file(m_sslctx, keyfile.c_str(), SSL_FILETYPE_PEM) != 1) { + + unsigned long errCode = ERR_get_error(); + char buffer[256]; + ERR_error_string_n(errCode, buffer, sizeof(buffer)); + vmime::string sslErr(buffer); + std::ostringstream oss; + oss << "Error loading private key from file " << keyfile; + oss << " - msg: " << sslErr; + throw security::cert::certificateException(oss.str()); + } +} + + +void TLSSession_OpenSSL::useCertificateChainFile(const vmime::string& chainFile) { + + ERR_clear_error(); + + if (SSL_CTX_use_certificate_chain_file(m_sslctx, chainFile.c_str()) != 1) { + + unsigned long errCode = ERR_get_error(); + char buffer[256]; + ERR_error_string_n(errCode, buffer, sizeof(buffer)); + vmime::string sslErr(buffer); + std::ostringstream oss; + oss << "Error loading certificate from file " << chainFile; + oss << " - msg: " << sslErr; + throw security::cert::certificateException(oss.str()); + } +} + + +SSL_CTX* TLSSession_OpenSSL::getContext() const { + + return m_sslctx; +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp b/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp new file mode 100644 index 0000000..518216b --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp @@ -0,0 +1,109 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_NET_TLS_TLSSESSION_OPENSSL_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSESSION_OPENSSL_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/TLSSession.hpp" +#include "vmime/net/tls/TLSSocket.hpp" +#include "vmime/net/tls/TLSProperties.hpp" + + +#include <openssl/ssl.h> + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession_OpenSSL : public TLSSession { + + friend class TLSSocket_OpenSSL; + +public: + + TLSSession_OpenSSL( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props + ); + + ~TLSSession_OpenSSL(); + + + shared_ptr <TLSSocket> getSocket(const shared_ptr <socket>& sok); + + shared_ptr <security::cert::certificateVerifier> getCertificateVerifier(); + + + /** Set the private key to use if server requires a client certificate. + * + * @param keyfile path to the private key in PEM format + */ + void usePrivateKeyFile(const vmime::string& keyfile); + + /** Supply the certificate chain to present if requested by server. + * + * @param chainFile File in PEM format holding certificate chain + */ + void useCertificateChainFile(const vmime::string& chainFile); + + /** Get a pointer to the SSL_CTX used for this session. + * + * @return the SSL_CTX used for all connections created with this session + */ + SSL_CTX* getContext() const; + +private: + + TLSSession_OpenSSL(const TLSSession_OpenSSL&); + + SSL_CTX* m_sslctx; + + shared_ptr <security::cert::certificateVerifier> m_certVerifier; + shared_ptr <TLSProperties> m_props; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSSESSION_OPENSSL_HPP_INCLUDED diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp b/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp new file mode 100644 index 0000000..978f0ca --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp @@ -0,0 +1,761 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include <openssl/ssl.h> +#include <openssl/err.h> + +#include "vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp" +#include "vmime/net/tls/openssl/TLSSession_OpenSSL.hpp" +#include "vmime/net/tls/openssl/OpenSSLInitializer.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp" + +#include "vmime/utility/stringUtils.hpp" + +#include <vector> +#include <cstring> + + +namespace vmime { +namespace net { +namespace tls { + + +static OpenSSLInitializer::autoInitializer openSSLInitializer; + + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +// static +BIO_METHOD TLSSocket_OpenSSL::sm_customBIOMethod = { + 100 | BIO_TYPE_SOURCE_SINK, + "vmime::socket glue", + TLSSocket_OpenSSL::bio_write, + TLSSocket_OpenSSL::bio_read, + TLSSocket_OpenSSL::bio_puts, + NULL, // gets + TLSSocket_OpenSSL::bio_ctrl, + TLSSocket_OpenSSL::bio_create, + TLSSocket_OpenSSL::bio_destroy, + 0 +}; + +#define BIO_set_init(b, val) b->init = val +#define BIO_set_data(b, val) b->ptr = val +#define BIO_set_num(b, val) b->num = val +#define BIO_set_flags(b, val) b->flags = val +#define BIO_set_shutdown(b, val) b->shutdown = val +#define BIO_get_init(b) b->init +#define BIO_get_data(b) b->ptr +#define BIO_get_shutdown(b) b->shutdown + +#else + +#define BIO_set_num(b, val) + +#endif + + + +// static +shared_ptr <TLSSocket> TLSSocket::wrap( + const shared_ptr <TLSSession>& session, + const shared_ptr <socket>& sok +) { + + return make_shared <TLSSocket_OpenSSL>(dynamicCast <TLSSession_OpenSSL>(session), sok); +} + + +TLSSocket_OpenSSL::TLSSocket_OpenSSL( + const shared_ptr <TLSSession_OpenSSL>& session, + const shared_ptr <socket>& sok +) + : m_session(session), + m_wrapped(sok), + m_connected(false), + m_ssl(0), + m_status(0), + m_ex() { + +} + + +TLSSocket_OpenSSL::~TLSSocket_OpenSSL() { + + try { + disconnect(); + } catch (...) { + // Don't throw in destructor + } +} + + +void TLSSocket_OpenSSL::createSSLHandle() { + + if (m_wrapped->isConnected()) { + string peerName = getPeerName(); + + if (peerName.empty()) { + throw exceptions::tls_exception("Unknown host name, will not be able to set SNI"); + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + + BIO* sockBio = BIO_new(&sm_customBIOMethod); + sockBio->ptr = this; + sockBio->init = 1; + +#else + + BIO_METHOD* bioMeth = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "vmime::socket glue"); + + if (!bioMeth) { + BIO_meth_free(bioMeth); + throw exceptions::tls_exception("BIO_meth_new() failed"); + } + + BIO_meth_set_write(bioMeth, TLSSocket_OpenSSL::bio_write); + BIO_meth_set_read(bioMeth, TLSSocket_OpenSSL::bio_read); + BIO_meth_set_puts(bioMeth, TLSSocket_OpenSSL::bio_puts); + BIO_meth_set_ctrl(bioMeth, TLSSocket_OpenSSL::bio_ctrl); + BIO_meth_set_create(bioMeth, TLSSocket_OpenSSL::bio_create); + BIO_meth_set_destroy(bioMeth, TLSSocket_OpenSSL::bio_destroy); + + BIO* sockBio = BIO_new(bioMeth); + BIO_set_data(sockBio, this); + BIO_set_init(sockBio, 1); + +#endif + + if (!sockBio) { + throw exceptions::tls_exception("BIO_new() failed"); + } + + m_ssl = SSL_new(m_session->getContext()); + + if (!m_ssl) { + BIO_free(sockBio); + throw exceptions::tls_exception("Cannot create SSL object"); + } + + SSL_set_bio(m_ssl, sockBio, sockBio); + SSL_set_tlsext_host_name(m_ssl, peerName.c_str()); + SSL_set_connect_state(m_ssl); + SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + } else { + + throw exceptions::tls_exception("Unconnected socket error"); + } +} + + +void TLSSocket_OpenSSL::connect(const string& address, const port_t port) { + + try { + + m_wrapped->connect(address, port); + + createSSLHandle(); + + handshake(); + + } catch (...) { + + disconnect(); + throw; + } +} + + +void TLSSocket_OpenSSL::disconnect() { + + if (m_ssl) { + + // Don't shut down the socket more than once. + int shutdownState = SSL_get_shutdown(m_ssl); + bool shutdownSent = (shutdownState & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN; + + if (!shutdownSent) { + SSL_shutdown(m_ssl); + } + + SSL_free(m_ssl); + m_ssl = 0; + } + + if (m_connected) { + m_connected = false; + m_wrapped->disconnect(); + } +} + + +bool TLSSocket_OpenSSL::isConnected() const { + + return m_wrapped->isConnected() && m_connected; +} + + +size_t TLSSocket_OpenSSL::getBlockSize() const { + + return 16384; // 16 KB +} + + +const string TLSSocket_OpenSSL::getPeerName() const { + + return m_wrapped->getPeerName(); +} + + +const string TLSSocket_OpenSSL::getPeerAddress() const { + + return m_wrapped->getPeerAddress(); +} + + +shared_ptr <timeoutHandler> TLSSocket_OpenSSL::getTimeoutHandler() { + + return m_wrapped->getTimeoutHandler(); +} + + +void TLSSocket_OpenSSL::setTracer(const shared_ptr <net::tracer>& tracer) { + + m_wrapped->setTracer(tracer); +} + + +shared_ptr <net::tracer> TLSSocket_OpenSSL::getTracer() { + + return m_wrapped->getTracer(); +} + + +bool TLSSocket_OpenSSL::waitForRead(const int msecs) { + + return m_wrapped->waitForRead(msecs); +} + + +bool TLSSocket_OpenSSL::waitForWrite(const int msecs) { + + return m_wrapped->waitForWrite(msecs); +} + + +void TLSSocket_OpenSSL::receive(string& buffer) { + + const size_t size = receiveRaw(m_buffer, sizeof(m_buffer)); + + if (size != 0) { + buffer = utility::stringUtils::makeStringFromBytes(m_buffer, size); + } else { + buffer.clear(); + } +} + + +void TLSSocket_OpenSSL::send(const string& buffer) { + + sendRaw(reinterpret_cast <const byte_t*>(buffer.data()), buffer.length()); +} + + +void TLSSocket_OpenSSL::send(const char* str) { + + sendRaw(reinterpret_cast <const byte_t*>(str), ::strlen(str)); +} + + +size_t TLSSocket_OpenSSL::receiveRaw(byte_t* buffer, const size_t count) { + + if (!m_ssl) { + throw exceptions::socket_not_connected_exception(); + } + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + ERR_clear_error(); + int rc = SSL_read(m_ssl, buffer, static_cast <int>(count)); + + if (m_ex.get()) { + internalThrow(); + } + + if (rc <= 0) { + + int error = SSL_get_error(m_ssl, rc); + + if (error == SSL_ERROR_WANT_WRITE) { + m_status |= STATUS_WANT_WRITE; + return 0; + } else if (error == SSL_ERROR_WANT_READ) { + m_status |= STATUS_WANT_READ; + return 0; + } + + handleError(rc); + } + + return rc; +} + + +void TLSSocket_OpenSSL::sendRaw(const byte_t* buffer, const size_t count) { + + if (!m_ssl) { + throw exceptions::socket_not_connected_exception(); + } + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + for (size_t size = count ; size > 0 ; ) { + + ERR_clear_error(); + int rc = SSL_write(m_ssl, buffer, static_cast <int>(size)); + + if (rc <= 0) { + + int error = SSL_get_error(m_ssl, rc); + + if (error == SSL_ERROR_WANT_READ) { + m_wrapped->waitForRead(); + continue; + } else if (error == SSL_ERROR_WANT_WRITE) { + m_wrapped->waitForWrite(); + continue; + } + + handleError(rc); + + } else { + + buffer += rc; + size -= rc; + } + } +} + + +size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t count) { + + if (!m_ssl) { + throw exceptions::socket_not_connected_exception(); + } + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + ERR_clear_error(); + int rc = SSL_write(m_ssl, buffer, static_cast <int>(count)); + + if (m_ex.get()) { + internalThrow(); + } + + if (rc <= 0) { + + int error = SSL_get_error(m_ssl, rc); + + if (error == SSL_ERROR_WANT_WRITE) { + m_status |= STATUS_WANT_WRITE; + return 0; + } else if (error == SSL_ERROR_WANT_READ) { + m_status |= STATUS_WANT_READ; + return 0; + } + + handleError(rc); + } + + return rc; +} + + +void TLSSocket_OpenSSL::handshake() { + + shared_ptr <timeoutHandler> toHandler = m_wrapped->getTimeoutHandler(); + + if (toHandler) { + toHandler->resetTimeOut(); + } + + if (getTracer()) { + getTracer()->traceSend("Beginning SSL/TLS handshake"); + } + + // Start handshaking process + if (!m_ssl) { + createSSLHandle(); + } + + try { + + int rc; + + ERR_clear_error(); + + while ((rc = SSL_do_handshake(m_ssl)) <= 0) { + + const int err = SSL_get_error(m_ssl, rc); + + if (err == SSL_ERROR_WANT_READ) { + m_wrapped->waitForRead(); + } else if (err == SSL_ERROR_WANT_WRITE) { + m_wrapped->waitForWrite(); + } else { + handleError(rc); + } + + // Check whether the time-out delay is elapsed + if (toHandler && toHandler->isTimeOut()) { + + if (!toHandler->handleTimeOut()) { + throw exceptions::operation_timed_out(); + } + + toHandler->resetTimeOut(); + } + + ERR_clear_error(); + } + + } catch (...) { + + throw; + } + + // Verify server's certificate(s) + shared_ptr <security::cert::certificateChain> certs = getPeerCertificates(); + + if (!certs) { + throw exceptions::tls_exception("No peer certificate."); + } + + m_session->getCertificateVerifier()->verify(certs, getPeerName()); + + m_connected = true; +} + + +shared_ptr <security::cert::certificateChain> TLSSocket_OpenSSL::getPeerCertificates() { + + if (getTracer()) { + getTracer()->traceSend("Getting peer certificates"); + } + + STACK_OF(X509)* chain = SSL_get_peer_cert_chain(m_ssl); + + if (chain == NULL) { + return null; + } + + int certCount = sk_X509_num(chain); + + if (certCount == 0) { + return null; + } + + bool error = false; + std::vector <shared_ptr <security::cert::certificate> > certs; + + for (int i = 0; i < certCount && !error; i++) { + + shared_ptr <vmime::security::cert::X509Certificate> cert = + vmime::security::cert::X509Certificate_OpenSSL::importInternal(sk_X509_value(chain, i)); + + if (cert) { + certs.push_back(cert); + } else { + error = true; + } + } + + if (error) { + return null; + } + + return make_shared <security::cert::certificateChain>(certs); +} + + +void TLSSocket_OpenSSL::internalThrow() { + + if (m_ex.get()) { + throw *m_ex; + } +} + + +void TLSSocket_OpenSSL::handleError(int rc) { + + if (rc > 0) { + return; + } + + internalThrow(); + + int sslError = SSL_get_error(m_ssl, rc); + long lastError = ERR_get_error(); + + switch (sslError) { + + case SSL_ERROR_ZERO_RETURN: + + disconnect(); + return; + + case SSL_ERROR_SYSCALL: { + + if (lastError == 0) { + + if (rc == 0) { + + throw exceptions::tls_exception("SSL connection unexpectedly closed"); + + } else { + + std::ostringstream oss; + oss << "The BIO reported an error: " << rc; + oss.flush(); + throw exceptions::tls_exception(oss.str()); + } + } + + break; + } + + case SSL_ERROR_WANT_READ: + + BIO_set_retry_read(SSL_get_rbio(m_ssl)); + break; + + case SSL_ERROR_WANT_WRITE: + + BIO_set_retry_write(SSL_get_wbio(m_ssl)); + break; + + // This happens only for BIOs of type BIO_s_connect() or BIO_s_accept() + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + // SSL_CTX_set_client_cert_cb related, not used + case SSL_ERROR_WANT_X509_LOOKUP: + case SSL_ERROR_SSL: + default: + + if (lastError == 0) { + + throw exceptions::tls_exception("Unexpected SSL IO error"); + + } else { + + char buffer[256]; + ERR_error_string_n(lastError, buffer, sizeof(buffer)); + vmime::string msg(buffer); + throw exceptions::tls_exception(msg); + } + + break; + } +} + + +unsigned int TLSSocket_OpenSSL::getStatus() const { + + return m_status; +} + + +// Implementation of custom BIO methods + + +// static +int TLSSocket_OpenSSL::bio_write(BIO* bio, const char* buf, int len) { + + BIO_clear_retry_flags(bio); + + if (buf == NULL || len <= 0) { + return -1; + } + + TLSSocket_OpenSSL *sok = reinterpret_cast <TLSSocket_OpenSSL*>(BIO_get_data(bio)); + + if (!BIO_get_init(bio) || !sok) { + return -1; + } + + try { + + const size_t n = sok->m_wrapped->sendRawNonBlocking( + reinterpret_cast <const byte_t*>(buf), len + ); + + if (n == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) { + BIO_set_retry_write(bio); + return -1; + } + + return static_cast <int>(n); + + } catch (exception& e) { + + // Workaround for passing C++ exceptions from C BIO functions + sok->m_ex.reset(e.clone()); + return -1; + } +} + + +// static +int TLSSocket_OpenSSL::bio_read(BIO* bio, char* buf, int len) { + + BIO_clear_retry_flags(bio); + + if (buf == NULL || len <= 0) { + return -1; + } + + TLSSocket_OpenSSL *sok = reinterpret_cast <TLSSocket_OpenSSL*>(BIO_get_data(bio)); + + if (!BIO_get_init(bio) || !sok) { + return -1; + } + + try { + + const size_t n = sok->m_wrapped->receiveRaw( + reinterpret_cast <byte_t*>(buf), len + ); + + if (n == 0 || sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) { + BIO_set_retry_read(bio); + return -1; + } + + return static_cast <int>(n); + + } catch (exception& e) { + + // Workaround for passing C++ exceptions from C BIO functions + sok->m_ex.reset(e.clone()); + return -1; + } +} + + +// static +int TLSSocket_OpenSSL::bio_puts(BIO* bio, const char* str) { + + return bio_write(bio, str, static_cast <int>(strlen(str))); +} + + +// static +long TLSSocket_OpenSSL::bio_ctrl(BIO* bio, int cmd, long num, void* /* ptr */) { + + long ret = 1; + + switch (cmd) { + + case BIO_CTRL_INFO: + + ret = 0; + break; + + case BIO_CTRL_GET_CLOSE: + + ret = BIO_get_shutdown(bio); + break; + + case BIO_CTRL_SET_CLOSE: + + BIO_set_shutdown(bio, static_cast <int>(num)); + break; + + case BIO_CTRL_PENDING: + case BIO_CTRL_WPENDING: + + ret = 0; + break; + + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: + + ret = 1; + break; + + default: + + ret = 0; + break; + } + + return ret; +} + + +// static +int TLSSocket_OpenSSL::bio_create(BIO* bio) { + + BIO_set_init(bio, 0); + BIO_set_num(bio, 0); + BIO_set_data(bio, NULL); + BIO_set_flags(bio, 0); + + return 1; +} + + +// static +int TLSSocket_OpenSSL::bio_destroy(BIO* bio) { + + if (!bio) { + return 0; + } + + if (BIO_get_shutdown(bio)) { + BIO_set_data(bio, NULL); + BIO_set_init(bio, 0); + BIO_set_flags(bio, 0); + } + + return 1; +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL diff --git a/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp b/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp new file mode 100644 index 0000000..e30df68 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp @@ -0,0 +1,142 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_NET_TLS_TLSSOCKET_OPENSSL_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSOCKET_OPENSSL_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/net/tls/TLSSocket.hpp" + +#include <memory> + +#include <openssl/ssl.h> + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession; +class TLSSession_OpenSSL; + + +class TLSSocket_OpenSSL : public TLSSocket { + +public: + + TLSSocket_OpenSSL( + const shared_ptr <TLSSession_OpenSSL>& session, + const shared_ptr <socket>& sok + ); + + ~TLSSocket_OpenSSL(); + + + void handshake(); + + shared_ptr <security::cert::certificateChain> getPeerCertificates(); + + // Implementation of 'socket' + void connect(const string& address, const port_t port); + void disconnect(); + bool isConnected() const; + + bool waitForRead(const int msecs = 30000); + bool waitForWrite(const int msecs = 30000); + + void receive(string& buffer); + size_t receiveRaw(byte_t* buffer, const size_t count); + + void send(const string& buffer); + void send(const char* str); + void sendRaw(const byte_t* buffer, const size_t count); + size_t sendRawNonBlocking(const byte_t* buffer, const size_t count); + + size_t getBlockSize() const; + + unsigned int getStatus() const; + + const string getPeerName() const; + const string getPeerAddress() const; + + shared_ptr <timeoutHandler> getTimeoutHandler(); + + void setTracer(const shared_ptr <net::tracer>& tracer); + shared_ptr <net::tracer> getTracer(); + +private: + + static BIO_METHOD sm_customBIOMethod; + + static int bio_write(BIO* bio, const char* buf, int len); + static int bio_read(BIO* bio, char* buf, int len); + static int bio_puts(BIO* bio, const char* str); + static int bio_gets(BIO* bio, char* buf, int len); + static long bio_ctrl(BIO* bio, int cmd, long num, void* ptr); + static int bio_create(BIO* bio); + static int bio_destroy(BIO* bio); + + void createSSLHandle(); + + void internalThrow(); + void handleError(int rc); + + + shared_ptr <TLSSession_OpenSSL> m_session; + + shared_ptr <socket> m_wrapped; + + bool m_connected; + + byte_t m_buffer[65536]; + + SSL* m_ssl; + + unsigned int m_status; + + // Last exception thrown from C BIO functions + scoped_ptr <exception> m_ex; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSSOCKET_OPENSSL_HPP_INCLUDED + |