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/gnutls | |
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/gnutls')
6 files changed, 1266 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.cpp b/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.cpp new file mode 100644 index 0000000..b2996fb --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.cpp @@ -0,0 +1,113 @@ +// +// 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_GNUTLS + + +#include "vmime/base.hpp" +#include "vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp" + +#include <gnutls/gnutls.h> +#if GNUTLS_VERSION_NUMBER < 0x030000 +#include <gnutls/extra.h> +#endif + + +namespace vmime { +namespace net { +namespace tls { + + +TLSProperties::TLSProperties() + : m_data(make_shared <TLSProperties_GnuTLS>()) { + + setCipherSuite(CIPHERSUITE_DEFAULT); +} + + +TLSProperties::TLSProperties(const TLSProperties& props) + : object(), + m_data(make_shared <TLSProperties_GnuTLS>()) { + + *dynamicCast <TLSProperties_GnuTLS>(m_data) = *dynamicCast <TLSProperties_GnuTLS>(props.m_data); +} + + +void TLSProperties::setCipherSuite(const GenericCipherSuite cipherSuite) { + + switch (cipherSuite) { + + case CIPHERSUITE_HIGH: + + setCipherSuite("SECURE256:%SSL3_RECORD_VERSION"); + break; + + case CIPHERSUITE_MEDIUM: + + setCipherSuite("SECURE128:%SSL3_RECORD_VERSION"); + break; + + case CIPHERSUITE_LOW: + + setCipherSuite("NORMAL:%SSL3_RECORD_VERSION"); + break; + + default: + case CIPHERSUITE_DEFAULT: + + setCipherSuite("NORMAL:%SSL3_RECORD_VERSION"); + break; + } +} + + +void TLSProperties::setCipherSuite(const string& cipherSuite) { + + dynamicCast <TLSProperties_GnuTLS>(m_data)->cipherSuite = cipherSuite; +} + + +const string TLSProperties::getCipherSuite() const { + + return dynamicCast <TLSProperties_GnuTLS>(m_data)->cipherSuite; +} + + + +TLSProperties_GnuTLS& TLSProperties_GnuTLS::operator=(const TLSProperties_GnuTLS& other) { + + cipherSuite = other.cipherSuite; + + return *this; +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp b/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp new file mode 100644 index 0000000..96bbaea --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSProperties_GnuTLS.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_GNUTLS_HPP_INCLUDED +#define VMIME_NET_TLS_TLSPROPERTIES_GNUTLS_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/TLSProperties.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSProperties_GnuTLS : public object { + +public: + + TLSProperties_GnuTLS& operator=(const TLSProperties_GnuTLS& other); + + + string cipherSuite; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSPROPERTIES_GNUTLS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.cpp b/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.cpp new file mode 100644 index 0000000..8586537 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.cpp @@ -0,0 +1,313 @@ +// +// 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_GNUTLS + + +#include <gnutls/gnutls.h> +#if GNUTLS_VERSION_NUMBER < 0x030000 +#include <gnutls/extra.h> +#endif + + +// Dependency on gcrypt is not needed since GNU TLS version 2.12. +// See here: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=638651 +#if GNUTLS_VERSION_NUMBER <= 0x020b00 +# define VMIME_GNUTLS_NEEDS_GCRYPT 1 +#endif + +#if VMIME_HAVE_PTHREAD +# include <pthread.h> +# if VMIME_GNUTLS_NEEDS_GCRYPT +# include <gcrypt.h> +# endif +# include <errno.h> +#endif // VMIME_HAVE_PTHREAD + +#include "vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp" +#include "vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp" +#include "vmime/net/tls/gnutls/TLSProperties_GnuTLS.hpp" + +#include "vmime/exception.hpp" + + +// Enable GnuTLS debugging by defining GNUTLS_DEBUG +//#define GNUTLS_DEBUG 1 + + +#include <sstream> +#include <iomanip> + +#if VMIME_DEBUG && GNUTLS_DEBUG + #include <iostream> +#endif // VMIME_DEBUG && GNUTLS_DEBUG + + +#if VMIME_HAVE_PTHREAD && VMIME_GNUTLS_NEEDS_GCRYPT && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) +extern "C" +{ + GCRY_THREAD_OPTION_PTHREAD_IMPL; +} +#endif // VMIME_HAVE_PTHREAD && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL + + +namespace vmime { +namespace net { +namespace tls { + + +#ifndef VMIME_BUILDING_DOC + +// Initialize GNU TLS library +struct TLSGlobal { + + TLSGlobal() { + +#if VMIME_HAVE_PTHREAD && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) + #if VMIME_GNUTLS_NEEDS_GCRYPT + gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + #endif // VMIME_GNUTLS_NEEDS_GCRYPT +#endif // VMIME_HAVE_PTHREAD && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL + + gnutls_global_init(); + //gnutls_global_init_extra(); + +#if VMIME_DEBUG && GNUTLS_DEBUG + gnutls_global_set_log_function(TLSLogFunc); + gnutls_global_set_log_level(10); +#endif // VMIME_DEBUG && GNUTLS_DEBUG + + gnutls_anon_allocate_client_credentials(&anonCred); + gnutls_certificate_allocate_credentials(&certCred); + } + + ~TLSGlobal() { + + gnutls_anon_free_client_credentials(anonCred); + gnutls_certificate_free_credentials(certCred); + + gnutls_global_deinit(); + } + +#if VMIME_DEBUG && GNUTLS_DEBUG + + static void TLSLogFunc(int level, const char *str) { + + std::cerr << "GNUTLS: [" << level << "] " << str << std::endl; + } + +#endif // VMIME_DEBUG && GNUTLS_DEBUG + + + gnutls_anon_client_credentials_t anonCred; + gnutls_certificate_credentials_t certCred; +}; + +static TLSGlobal g_gnutlsGlobal; + + +#endif // VMIME_BUILDING_DOC + + + +// static +shared_ptr <TLSSession> TLSSession::create( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props +) { + + return make_shared <TLSSession_GnuTLS>(cv, props); +} + + +TLSSession_GnuTLS::TLSSession_GnuTLS( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props +) + : m_certVerifier(cv), + m_props(props) { + + int res; + + m_gnutlsSession = new gnutls_session_t; + + if (gnutls_init(m_gnutlsSession, GNUTLS_CLIENT) != 0) { + throw std::bad_alloc(); + } + + // Sets some default priority on the ciphers, key exchange methods, + // macs and compression methods. +#ifdef VMIME_HAVE_GNUTLS_PRIORITY_FUNCS + gnutls_dh_set_prime_bits(*m_gnutlsSession, 128); + + if ((res = gnutls_priority_set_direct + (*m_gnutlsSession, m_props->getCipherSuite().c_str(), NULL)) != 0) { + + throwTLSException("gnutls_priority_set_direct", res); + } + +#else // !VMIME_HAVE_GNUTLS_PRIORITY_FUNCS + + gnutls_set_default_priority(*m_gnutlsSession); + + // Sets the priority on the certificate types supported by gnutls. + // Priority is higher for types specified before others. After + // specifying the types you want, you must append a 0. + const int certTypePriority[] = { GNUTLS_CRT_X509, 0 }; + + res = gnutls_certificate_type_set_priority(*m_gnutlsSession, certTypePriority); + + if (res < 0) { + throwTLSException("gnutls_certificate_type_set_priority", res); + } + + // Sets the priority on the protocol types + const int protoPriority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 }; + + res = gnutls_protocol_set_priority(*m_gnutlsSession, protoPriority); + + if (res < 0) { + throwTLSException("gnutls_certificate_type_set_priority", res); + } + + // Priority on the ciphers + const int cipherPriority[] = { + GNUTLS_CIPHER_ARCFOUR_128, + GNUTLS_CIPHER_3DES_CBC, + GNUTLS_CIPHER_AES_128_CBC, + GNUTLS_CIPHER_AES_256_CBC, + GNUTLS_CIPHER_ARCFOUR_40, + GNUTLS_CIPHER_RC2_40_CBC, + GNUTLS_CIPHER_DES_CBC, + 0 + }; + + gnutls_cipher_set_priority(*m_gnutlsSession, cipherPriority); + + // Priority on MACs + const int macPriority[] = { + GNUTLS_MAC_SHA, + GNUTLS_MAC_MD5, + 0 + }; + + gnutls_mac_set_priority(*m_gnutlsSession, macPriority); + + // Priority on key exchange methods + const int kxPriority[] = { + GNUTLS_KX_RSA, + GNUTLS_KX_DHE_DSS, + GNUTLS_KX_DHE_RSA, + GNUTLS_KX_ANON_DH, + GNUTLS_KX_SRP, + GNUTLS_KX_RSA_EXPORT, + GNUTLS_KX_SRP_RSA, + GNUTLS_KX_SRP_DSS, + 0 + }; + + gnutls_kx_set_priority(*m_gnutlsSession, kxPriority); + + // Priority on compression methods + const int compressionPriority[] = { + GNUTLS_COMP_ZLIB, + //GNUTLS_COMP_LZO, + GNUTLS_COMP_NULL, + 0 + }; + + gnutls_compression_set_priority(*m_gnutlsSession, compressionPriority); + +#endif // !VMIME_HAVE_GNUTLS_PRIORITY_FUNCS + + // Initialize credentials + gnutls_credentials_set( + *m_gnutlsSession, GNUTLS_CRD_ANON, g_gnutlsGlobal.anonCred + ); + + gnutls_credentials_set( + *m_gnutlsSession, GNUTLS_CRD_CERTIFICATE, g_gnutlsGlobal.certCred + ); +} + + +TLSSession_GnuTLS::TLSSession_GnuTLS(const TLSSession_GnuTLS&) + : TLSSession() { + + // Not used +} + + +TLSSession_GnuTLS::~TLSSession_GnuTLS() { + + try { + + if (m_gnutlsSession) { + + gnutls_deinit(*m_gnutlsSession); + + delete m_gnutlsSession; + m_gnutlsSession = NULL; + } + + } catch (...) { + + // Don't throw in destructor + } +} + + +shared_ptr <TLSSocket> TLSSession_GnuTLS::getSocket(const shared_ptr <socket>& sok) { + + return TLSSocket::wrap(dynamicCast <TLSSession>(shared_from_this()), sok); +} + + +shared_ptr <security::cert::certificateVerifier> TLSSession_GnuTLS::getCertificateVerifier() { + + return m_certVerifier; +} + + +void TLSSession_GnuTLS::throwTLSException(const string& fname, const int code) { + + std::ostringstream msg; + + msg << fname + "() returned code "; + msg << std::hex << code; + msg << ": "; + msg << gnutls_strerror(code); + + throw exceptions::tls_exception(msg.str()); +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp b/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp new file mode 100644 index 0000000..2a7f9d7 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp @@ -0,0 +1,95 @@ +// +// 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_GNUTLS_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSESSION_GNUTLS_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/TLSSession.hpp" +#include "vmime/net/tls/TLSSocket.hpp" +#include "vmime/net/tls/TLSProperties.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession_GnuTLS : public TLSSession { + + friend class TLSSocket_GnuTLS; + +public: + + TLSSession_GnuTLS( + const shared_ptr <security::cert::certificateVerifier>& cv, + const shared_ptr <TLSProperties>& props + ); + + ~TLSSession_GnuTLS(); + + + shared_ptr <TLSSocket> getSocket(const shared_ptr <socket>& sok); + + shared_ptr <security::cert::certificateVerifier> getCertificateVerifier(); + +private: + + TLSSession_GnuTLS(const TLSSession_GnuTLS&); + + static void throwTLSException(const string& fname, const int code); + + +#ifdef LIBGNUTLS_VERSION + gnutls_session_t* m_gnutlsSession; +#else + void* m_gnutlsSession; +#endif // LIBGNUTLS_VERSION + + 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_GNUTLS + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSSESSION_GNUTLS_HPP_INCLUDED + diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp b/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp new file mode 100644 index 0000000..53e4eae --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp @@ -0,0 +1,548 @@ +// +// 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_GNUTLS + + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include <errno.h> + +#include "vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp" +#include "vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/security/cert/X509Certificate.hpp" + +#include "vmime/utility/stringUtils.hpp" + +#include <cstring> + + +namespace vmime { +namespace net { +namespace tls { + + +// static +shared_ptr <TLSSocket> TLSSocket::wrap( + const shared_ptr <TLSSession>& session, + const shared_ptr <socket>& sok +) +{ + return make_shared <TLSSocket_GnuTLS>(dynamicCast <TLSSession_GnuTLS>(session), sok); +} + + +TLSSocket_GnuTLS::TLSSocket_GnuTLS( + const shared_ptr <TLSSession_GnuTLS>& session, + const shared_ptr <socket>& sok +) + : m_session(session), + m_wrapped(sok), + m_connected(false), + m_ex(NULL), + m_status(0), + m_errno(0) { + + gnutls_transport_set_ptr(*m_session->m_gnutlsSession, this); + + gnutls_transport_set_push_function(*m_session->m_gnutlsSession, gnutlsPushFunc); + gnutls_transport_set_pull_function(*m_session->m_gnutlsSession, gnutlsPullFunc); + gnutls_transport_set_errno_function(*m_session->m_gnutlsSession, gnutlsErrnoFunc); +} + + +TLSSocket_GnuTLS::~TLSSocket_GnuTLS() { + + resetException(); + + try { + disconnect(); + } catch (...) { + // Don't throw exception in destructor + } +} + + +void TLSSocket_GnuTLS::connect(const string& address, const port_t port) { + + try { + + m_wrapped->connect(address, port); + + handshake(); + + } catch (...) { + + disconnect(); + throw; + } +} + + +void TLSSocket_GnuTLS::disconnect() { + + if (m_connected) { + + gnutls_bye(*m_session->m_gnutlsSession, GNUTLS_SHUT_RDWR); + + m_wrapped->disconnect(); + + m_connected = false; + } +} + + +bool TLSSocket_GnuTLS::isConnected() const { + + return m_wrapped->isConnected() && m_connected; +} + + +size_t TLSSocket_GnuTLS::getBlockSize() const { + + return 16384; // 16 KB +} + + +const string TLSSocket_GnuTLS::getPeerName() const { + + return m_wrapped->getPeerName(); +} + + +const string TLSSocket_GnuTLS::getPeerAddress() const { + + return m_wrapped->getPeerAddress(); +} + + +shared_ptr <timeoutHandler> TLSSocket_GnuTLS::getTimeoutHandler() { + + return m_wrapped->getTimeoutHandler(); +} + + +void TLSSocket_GnuTLS::setTracer(const shared_ptr <net::tracer>& tracer) { + + m_wrapped->setTracer(tracer); +} + + +shared_ptr <net::tracer> TLSSocket_GnuTLS::getTracer() { + + return m_wrapped->getTracer(); +} + + +bool TLSSocket_GnuTLS::waitForRead(const int msecs) { + + return m_wrapped->waitForRead(msecs); +} + + +bool TLSSocket_GnuTLS::waitForWrite(const int msecs) { + + return m_wrapped->waitForWrite(msecs); +} + + +void TLSSocket_GnuTLS::receive(string& buffer) { + + const size_t size = receiveRaw(m_buffer, sizeof(m_buffer)); + buffer = utility::stringUtils::makeStringFromBytes(m_buffer, size); +} + + +void TLSSocket_GnuTLS::send(const string& buffer) { + + sendRaw(reinterpret_cast <const byte_t*>(buffer.data()), buffer.length()); +} + + +void TLSSocket_GnuTLS::send(const char* str) { + + sendRaw(reinterpret_cast <const byte_t*>(str), ::strlen(str)); +} + + +size_t TLSSocket_GnuTLS::receiveRaw(byte_t* buffer, const size_t count) { + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + resetException(); + + const ssize_t ret = gnutls_record_recv( + *m_session->m_gnutlsSession, + buffer, static_cast <size_t>(count) + ); + + throwException(); + + if (ret < 0) { + + if (ret == GNUTLS_E_AGAIN) { + + if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0) { + m_status |= STATUS_WANT_READ; + } else { + m_status |= STATUS_WANT_WRITE; + } + + return 0; + } + + TLSSession_GnuTLS::throwTLSException("gnutls_record_recv", static_cast <int>(ret)); + } + + return static_cast <size_t>(ret); +} + + +void TLSSocket_GnuTLS::sendRaw(const byte_t* buffer, const size_t count) { + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + for (size_t size = count ; size > 0 ; ) { + + resetException(); + + ssize_t ret = gnutls_record_send( + *m_session->m_gnutlsSession, + buffer, static_cast <size_t>(size) + ); + + throwException(); + + if (ret < 0) { + + if (ret == GNUTLS_E_AGAIN) { + + if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0) { + m_wrapped->waitForRead(); + } else { + m_wrapped->waitForWrite(); + } + + continue; + } + + TLSSession_GnuTLS::throwTLSException("gnutls_record_send", static_cast <int>(ret)); + + } else { + + buffer += ret; + size -= ret; + } + } +} + + +size_t TLSSocket_GnuTLS::sendRawNonBlocking(const byte_t* buffer, const size_t count) { + + m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); + + resetException(); + + ssize_t ret = gnutls_record_send( + *m_session->m_gnutlsSession, + buffer, static_cast <size_t>(count) + ); + + throwException(); + + if (ret < 0) { + + if (ret == GNUTLS_E_AGAIN) { + + if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0) { + m_status |= STATUS_WANT_READ; + } else { + m_status |= STATUS_WANT_WRITE; + } + + return 0; + } + + TLSSession_GnuTLS::throwTLSException("gnutls_record_send", static_cast <int>(ret)); + } + + return static_cast <size_t>(ret); +} + + +unsigned int TLSSocket_GnuTLS::getStatus() const { + + return m_status | m_wrapped->getStatus(); +} + + +void TLSSocket_GnuTLS::handshake() { + + shared_ptr <timeoutHandler> toHandler = m_wrapped->getTimeoutHandler(); + + if (toHandler) { + toHandler->resetTimeOut(); + } + + if (getTracer()) { + getTracer()->traceSend("Beginning SSL/TLS handshake"); + } + + // Start handshaking process + try { + string peerName = getPeerName(); + + gnutls_server_name_set(*m_session->m_gnutlsSession, GNUTLS_NAME_DNS, peerName.c_str(), peerName.size()); + + while (true) { + + resetException(); + + const int ret = gnutls_handshake(*m_session->m_gnutlsSession); + + throwException(); + + if (ret < 0) { + + if (ret == GNUTLS_E_AGAIN) { + + if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0) { + m_wrapped->waitForRead(); + } else { + m_wrapped->waitForWrite(); + } + + } else if (ret == GNUTLS_E_INTERRUPTED) { + + // Non-fatal error + + } else { + + TLSSession_GnuTLS::throwTLSException("gnutls_handshake", ret); + } + + } else { + + // Successful handshake + break; + } + } + + } catch (...) { + + throw; + } + + // Verify server's certificate(s) + shared_ptr <security::cert::certificateChain> certs = getPeerCertificates(); + + if (certs == NULL) { + throw exceptions::tls_exception("No peer certificate."); + } + + m_session->getCertificateVerifier()->verify(certs, getPeerName()); + + m_connected = true; +} + + +int TLSSocket_GnuTLS::gnutlsErrnoFunc(gnutls_transport_ptr_t trspt) { + + TLSSocket_GnuTLS* sok = reinterpret_cast <TLSSocket_GnuTLS*>(trspt); + return sok->m_errno; +} + + +ssize_t TLSSocket_GnuTLS::gnutlsPushFunc( + gnutls_transport_ptr_t trspt, + const void* data, + size_t len +) { + + TLSSocket_GnuTLS* sok = reinterpret_cast <TLSSocket_GnuTLS*>(trspt); + + try { + + const ssize_t ret = static_cast <ssize_t>( + sok->m_wrapped->sendRawNonBlocking(reinterpret_cast <const byte_t*>(data), len) + ); + + if (ret == 0) { + + gnutls_transport_set_errno(*sok->m_session->m_gnutlsSession, EAGAIN); + sok->m_errno = EAGAIN; + return -1; + } + + return ret; + + } catch (exception& e) { + + // Workaround for non-portable behaviour when throwing C++ exceptions + // from C functions (GNU TLS) + sok->m_ex = e.clone(); + return -1; + } +} + + +ssize_t TLSSocket_GnuTLS::gnutlsPullFunc( + gnutls_transport_ptr_t trspt, + void* data, + size_t len +) { + + TLSSocket_GnuTLS* sok = reinterpret_cast <TLSSocket_GnuTLS*>(trspt); + + try { + + const ssize_t n = static_cast <ssize_t>( + sok->m_wrapped->receiveRaw(reinterpret_cast <byte_t*>(data), len) + ); + + if (n == 0) { + + gnutls_transport_set_errno(*sok->m_session->m_gnutlsSession, EAGAIN); + sok->m_errno = EAGAIN; + return -1; + } + + return n; + + } catch (exception& e) { + + // Workaround for non-portable behaviour when throwing C++ exceptions + // from C functions (GNU TLS) + sok->m_ex = e.clone(); + return -1; + } +} + + +shared_ptr <security::cert::certificateChain> TLSSocket_GnuTLS::getPeerCertificates() { + + if (getTracer()) { + getTracer()->traceSend("Getting peer certificates"); + } + + unsigned int certCount = 0; + const gnutls_datum_t* rawData = gnutls_certificate_get_peers( + *m_session->m_gnutlsSession, &certCount + ); + + if (rawData == NULL) { + return null; + } + + // Try X.509 + gnutls_x509_crt_t* x509Certs = new gnutls_x509_crt_t[certCount]; + + for (unsigned int i = 0; i < certCount; ++i) { + + gnutls_x509_crt_init(x509Certs + i); + + int res = gnutls_x509_crt_import(x509Certs[i], rawData + i, GNUTLS_X509_FMT_DER); + + if (res < 0) { + + for (unsigned int j = 0 ; j <= i ; ++j) { + gnutls_x509_crt_deinit(x509Certs[j]); + } + + // XXX more fine-grained error reporting? + delete [] x509Certs; + return null; + } + } + + std::vector <shared_ptr <security::cert::certificate> > certs; + bool error = false; + + for (unsigned int i = 0 ; i < certCount ; ++i) { + + size_t dataSize = 0; + + gnutls_x509_crt_export(x509Certs[i], GNUTLS_X509_FMT_DER, NULL, &dataSize); + + std::vector <byte_t> data(dataSize); + + gnutls_x509_crt_export(x509Certs[i], GNUTLS_X509_FMT_DER, &data[0], &dataSize); + + shared_ptr <security::cert::X509Certificate> cert = + security::cert::X509Certificate::import(&data[0], dataSize); + + if (cert != NULL) { + certs.push_back(cert); + } else { + error = true; + } + + gnutls_x509_crt_deinit(x509Certs[i]); + } + + delete [] x509Certs; + + if (error) { + return null; + } + + return make_shared <security::cert::certificateChain>(certs); +} + + +// Following is a workaround for C++ exceptions to pass correctly between +// C and C++ calls. +// +// gnutls_record_recv() calls TLSSocket::gnutlsPullFunc, and exceptions +// thrown by the socket can not be caught. + +void TLSSocket_GnuTLS::throwException() { + + if (m_ex) { + throw *m_ex; + } +} + + +void TLSSocket_GnuTLS::resetException() { + + if (m_ex) { + delete m_ex; + m_ex = NULL; + } +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS diff --git a/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp b/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp new file mode 100644 index 0000000..0ac3e70 --- /dev/null +++ b/vmime-master/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp @@ -0,0 +1,129 @@ +// +// 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_GNUTLS_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSOCKET_GNUTLS_HPP_INCLUDED + + +#ifndef VMIME_BUILDING_DOC + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + + +#include "vmime/net/tls/TLSSocket.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession; +class TLSSession_GnuTLS; + + +class TLSSocket_GnuTLS : public TLSSocket { + +public: + + TLSSocket_GnuTLS(const shared_ptr <TLSSession_GnuTLS>& session, const shared_ptr <socket>& sok); + ~TLSSocket_GnuTLS(); + + + 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: + + void resetException(); + void throwException(); + +#ifdef LIBGNUTLS_VERSION + static ssize_t gnutlsPushFunc(gnutls_transport_ptr_t trspt, const void* data, size_t len); + static ssize_t gnutlsPullFunc(gnutls_transport_ptr_t trspt, void* data, size_t len); + static int gnutlsErrnoFunc(gnutls_transport_ptr_t trspt); +#else + static int gnutlsPushFunc(void* trspt, const void* data, size_t len); + static int gnutlsPullFunc(void* trspt, void* data, size_t len); + static int gnutlsErrnoFunc(void* trspt); +#endif // LIBGNUTLS_VERSION + + + shared_ptr <TLSSession_GnuTLS> m_session; + shared_ptr <socket> m_wrapped; + + bool m_connected; + + byte_t m_buffer[65536]; + + exception* m_ex; + + unsigned int m_status; + int m_errno; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + +#endif // VMIME_BUILDING_DOC + +#endif // VMIME_NET_TLS_TLSSOCKET_GNUTLS_HPP_INCLUDED + |