diff --git a/AUTHORS b/AUTHORS index bbddb306..98406a08 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,7 +2,7 @@ VMIME AUTHOR ============ -Vincent Richard +Vincent Richard Project owner and creator. VMime was created in 1998, and publicly released under the GNU GPL license in 2003. @@ -28,9 +28,10 @@ AUTHORS file. - Zarafa - Bartek Szurgot - Achim Brandt + - Mehmet Bozkurt (OpenSSL support) Please apologize if I have forgotten someone here. ;) Send me an email -to if you want your name to be listed. +to if you want your name to be listed. -See SVN Changelog for full list. +See Changelogs for full list. diff --git a/CMakeLists.txt b/CMakeLists.txt index af636051..5be9ca32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -448,6 +448,7 @@ ENDIF() # SSL/TLS support INCLUDE(FindGnuTLS) +INCLUDE(FindOpenSSL) INCLUDE(CheckFunctionExists) @@ -457,7 +458,7 @@ CHECK_FUNCTION_EXISTS(gnutls_priority_set_direct VMIME_HAVE_GNUTLS_PRIORITY_FUNC OPTION( VMIME_HAVE_TLS_SUPPORT - "SSL/TLS support (requires GNU TLS library)" + "SSL/TLS support (requires either GNU TLS or OpenSSL library)" ON ) @@ -467,6 +468,11 @@ OPTION( ON ) +OPTION( + VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + "Use OpenSSL library for SSL/TLS support" + OFF +) IF(VMIME_HAVE_TLS_SUPPORT) @@ -492,6 +498,23 @@ IF(VMIME_HAVE_TLS_SUPPORT) SET(VMIME_PKGCONFIG_CFLAGS "${VMIME_PKGCONFIG_CFLAGS} ${GNUTLS_INCLUDE_DIR}") SET(VMIME_PKGCONFIG_REQUIRES "${VMIME_PKGCONFIG_REQUIRES} libgnutls") + ELSEIF(VMIME_TLS_SUPPORT_LIB_IS_OPENSSL) + + INCLUDE_DIRECTORIES( + ${INCLUDE_DIRECTORIES} + ${OPENSSL_INCLUDE_DIR} + ) + + TARGET_LINK_LIBRARIES( + ${VMIME_LIBRARY_NAME} + ${TARGET_LINK_LIBRARIES} + ${OPENSSL_LIBRARIES} + ) + + SET(VMIME_PKGCONFIG_LIBS "${VMIME_PKGCONFIG_LIBS} ${OPENSSL_LIBRARIES}") + SET(VMIME_PKGCONFIG_CFLAGS "${VMIME_PKGCONFIG_CFLAGS} ${OPENSSL_INCLUDE_DIR}") + SET(VMIME_PKGCONFIG_REQUIRES "${VMIME_PKGCONFIG_REQUIRES} libopenssl") + ELSE() MESSAGE(FATAL_ERROR "TLS support is enabled, but no TLS/SSL library was selected/found") @@ -535,8 +558,14 @@ ENDIF() # POSIX-specific checks INCLUDE(CheckFunctionExists) +INCLUDE(CheckSymbolExists) + CHECK_FUNCTION_EXISTS(getaddrinfo VMIME_HAVE_GETADDRINFO) +CHECK_FUNCTION_EXISTS(gettid VMIME_HAVE_GETTID) +CHECK_FUNCTION_EXISTS(syscall VMIME_HAVE_SYSCALL) +CHECK_SYMBOL_EXISTS(SYS_gettid sys/syscall.h VMIME_HAVE_SYSCALL_GETTID) + FIND_PACKAGE(Threads) FIND_LIBRARY(PTHREAD pthread) diff --git a/SConstruct b/SConstruct index 0415ed79..31228ed4 100644 --- a/SConstruct +++ b/SConstruct @@ -137,6 +137,8 @@ libvmime_sources = [ 'utility/stringUtils.cpp', 'utility/stringUtils.hpp', 'utility/url.cpp', 'utility/url.hpp', 'utility/urlUtils.cpp', 'utility/urlUtils.hpp', + 'utility/sync/autoLock.hpp', + 'utility/sync/criticalSection.cpp', 'utility/sync/criticalSection.hpp', # -- encoder 'utility/encoder/encoder.cpp', 'utility/encoder/encoder.hpp', 'utility/encoder/sevenBitEncoder.cpp', 'utility/encoder/sevenBitEncoder.hpp', @@ -210,12 +212,19 @@ libvmime_messaging_sources = [ libvmime_net_tls_sources = [ 'net/tls/TLSSession.cpp', 'net/tls/TLSSession.hpp', 'net/tls/TLSSocket.cpp', 'net/tls/TLSSocket.hpp', + 'net/tls/gnutls/TLSSession_GnuTLS.cpp', 'net/tls/gnutls/TLSSession_GnuTLS.hpp', + 'net/tls/gnutls/TLSSocket_GnuTLS.cpp', 'net/tls/gnutls/TLSSocket_GnuTLS.hpp', + 'net/tls/openssl/TLSSession_OpenSSL.cpp', 'net/tls/openssl/TLSSession_OpenSSL.hpp', + 'net/tls/openssl/TLSSocket_OpenSSL.cpp', 'net/tls/openssl/TLSSocket_OpenSSL.hpp', + 'net/tls/openssl/OpenSSLInitializer.cpp', 'net/tls/openssl/OpenSSLInitializer.hpp', 'net/tls/TLSSecuredConnectionInfos.cpp', 'net/tls/TLSSecuredConnectionInfos.hpp', 'security/cert/certificateChain.cpp', 'security/cert/certificateChain.hpp', 'security/cert/certificateVerifier.hpp', 'security/cert/defaultCertificateVerifier.cpp', 'security/cert/defaultCertificateVerifier.hpp', 'security/cert/certificate.hpp', - 'security/cert/X509Certificate.cpp', 'security/cert/X509Certificate.hpp' + 'security/cert/X509Certificate.cpp', 'security/cert/X509Certificate.hpp', + 'security/cert/gnutls/X509Certificate_GnuTLS.cpp', 'security/cert/gnutls/X509Certificate_GnuTLS.hpp', + 'security/cert/openssl/X509Certificate_OpenSSL.cpp', 'security/cert/openssl/X509Certificate_OpenSSL.hpp' ] libvmime_messaging_proto_sources = [ @@ -282,12 +291,14 @@ libvmime_platforms_sources = { 'posix': [ 'platforms/posix/posixChildProcess.cpp', 'platforms/posix/posixChildProcess.hpp', + 'platforms/posix/posixCriticalSection.cpp', 'platforms/posix/posixCriticalSection.hpp', 'platforms/posix/posixFile.cpp', 'platforms/posix/posixFile.hpp', 'platforms/posix/posixHandler.cpp', 'platforms/posix/posixHandler.hpp', 'platforms/posix/posixSocket.cpp', 'platforms/posix/posixSocket.hpp' ], 'windows': [ + 'platforms/windows/windowsCriticalSection.cpp', 'platforms/windows/windowsCriticalSection.hpp', 'platforms/windows/windowsFile.cpp', 'platforms/windows/windowsFile.hpp', 'platforms/windows/windowsHandler.cpp', 'platforms/windows/windowsHandler.hpp', 'platforms/windows/windowsSocket.cpp', 'platforms/windows/windowsSocket.hpp' @@ -590,6 +601,7 @@ env.Append(CXXFLAGS = ['-Wpointer-arith']) env.Append(CXXFLAGS = ['-Wold-style-cast']) env.Append(CXXFLAGS = ['-Wconversion']) env.Append(CXXFLAGS = ['-Wcast-align']) +env.Append(CXXFLAGS = ['-Wno-long-long']) # OpenSSL #env.Append(CXXFLAGS = ['-Wshadow']) env.Append(TARFLAGS = ['-c']) @@ -614,6 +626,7 @@ if env['with_sasl'] == 'yes': env.ParseConfig('pkg-config --cflags --libs ' + libgsasl_pc) if env['with_tls'] == 'yes': + # GnuTLS libgnutls_pc = string.strip(os.popen("pkg-config --list-all | grep '^libgnutls[ ]' | cut -f 1 -d ' '").read()) if len(libgnutls_pc) == 0: @@ -625,6 +638,15 @@ if env['with_tls'] == 'yes': env.ParseConfig('pkg-config --cflags --libs ' + libgnutls_pc) + # OpenSSL + libopenssl_pc = string.strip(os.popen("pkg-config --list-all | grep '^openssl[ ]' | cut -f 1 -d ' '").read()) + + if len(libopenssl_pc) == 0: + print "ERROR: OpenSSL development package is not installed\n" + Exit(1) + + env.ParseConfig('pkg-config --cflags --libs ' + libopenssl_pc) + env.Append(CXXFLAGS = ['-pthread']) # Generate help text for command line options @@ -806,6 +828,7 @@ config_hpp.write('// -- TLS/SSL support\n') if env['with_tls'] == 'yes': config_hpp.write('#define VMIME_HAVE_TLS_SUPPORT 1\n') config_hpp.write('#define VMIME_TLS_SUPPORT_LIB_IS_GNUTLS 1\n') + config_hpp.write('#define VMIME_TLS_SUPPORT_LIB_IS_OPENSSL 0\n') config_hpp.write('#define VMIME_HAVE_GNUTLS_PRIORITY_FUNCS 1\n') else: config_hpp.write('#define VMIME_HAVE_TLS_SUPPORT 0\n') @@ -834,8 +857,13 @@ for platform in libvmime_platforms_sources: if not platform in platforms: config_hpp.write('#define VMIME_PLATFORM_IS_' + string.upper(platform) + ' 0\n') -config_hpp.write('#define VMIME_HAVE_GETADDRINFO 1\n') -config_hpp.write('#define VMIME_HAVE_PTHREAD 1\n') +config_hpp.write(""" +#define VMIME_HAVE_GETADDRINFO 1 +#define VMIME_HAVE_PTHREAD 1 +#define VMIME_HAVE_GETTID 0 +#define VMIME_HAVE_SYSCALL 1 +#define VMIME_HAVE_SYSCALL_GETTID 1 +""") config_hpp.write('\n') config_hpp.write('// Miscellaneous flags\n') diff --git a/cmake/config.hpp.cmake b/cmake/config.hpp.cmake index 66c26b6c..0b54fe97 100644 --- a/cmake/config.hpp.cmake +++ b/cmake/config.hpp.cmake @@ -43,6 +43,7 @@ typedef unsigned @VMIME_32BIT_TYPE@ vmime_uint32; // -- TLS/SSL support #cmakedefine01 VMIME_HAVE_TLS_SUPPORT #cmakedefine01 VMIME_TLS_SUPPORT_LIB_IS_GNUTLS +#cmakedefine01 VMIME_TLS_SUPPORT_LIB_IS_OPENSSL #define VMIME_HAVE_GNUTLS_PRIORITY_FUNCS @VMIME_HAVE_GNUTLS_PRIORITY_FUNCS@ // -- Messaging support #cmakedefine01 VMIME_HAVE_MESSAGING_FEATURES @@ -57,6 +58,9 @@ typedef unsigned @VMIME_32BIT_TYPE@ vmime_uint32; #cmakedefine01 VMIME_PLATFORM_IS_WINDOWS #cmakedefine01 VMIME_HAVE_PTHREAD #cmakedefine01 VMIME_HAVE_GETADDRINFO +#cmakedefine01 VMIME_HAVE_GETTID +#cmakedefine01 VMIME_HAVE_SYSCALL +#cmakedefine01 VMIME_HAVE_SYSCALL_GETTID #define VMIME_SENDMAIL_PATH "@VMIME_SENDMAIL_PATH@" diff --git a/src/net/imap/IMAPConnection.cpp b/src/net/imap/IMAPConnection.cpp index 9cb81d8c..6c8b400e 100644 --- a/src/net/imap/IMAPConnection.cpp +++ b/src/net/imap/IMAPConnection.cpp @@ -111,7 +111,7 @@ void IMAPConnection::connect() if (store->isIMAPS()) // dedicated port/IMAPS { ref tlsSession = - vmime::create (store->getCertificateVerifier()); + tls::TLSSession::create(store->getCertificateVerifier()); ref tlsSocket = tlsSession->getSocket(m_socket); @@ -460,7 +460,7 @@ void IMAPConnection::startTLS() } ref tlsSession = - vmime::create (m_store.acquire()->getCertificateVerifier()); + tls::TLSSession::create(m_store.acquire()->getCertificateVerifier()); ref tlsSocket = tlsSession->getSocket(m_socket); diff --git a/src/net/pop3/POP3Store.cpp b/src/net/pop3/POP3Store.cpp index 51181ece..8233cdb2 100644 --- a/src/net/pop3/POP3Store.cpp +++ b/src/net/pop3/POP3Store.cpp @@ -151,7 +151,7 @@ void POP3Store::connect() if (m_isPOP3S) // dedicated port/POP3S { ref tlsSession = - vmime::create (getCertificateVerifier()); + tls::TLSSession::create(getCertificateVerifier()); ref tlsSocket = tlsSession->getSocket(m_socket); @@ -550,7 +550,7 @@ void POP3Store::startTLS() throw exceptions::command_error("STLS", response); ref tlsSession = - vmime::create (getCertificateVerifier()); + tls::TLSSession::create(getCertificateVerifier()); ref tlsSocket = tlsSession->getSocket(m_socket); diff --git a/src/net/smtp/SMTPTransport.cpp b/src/net/smtp/SMTPTransport.cpp index 64028d1c..e658ecb7 100644 --- a/src/net/smtp/SMTPTransport.cpp +++ b/src/net/smtp/SMTPTransport.cpp @@ -114,7 +114,7 @@ void SMTPTransport::connect() if (m_isSMTPS) // dedicated port/SMTPS { ref tlsSession = - vmime::create (getCertificateVerifier()); + tls::TLSSession::create(getCertificateVerifier()); ref tlsSocket = tlsSession->getSocket(m_socket); @@ -463,7 +463,7 @@ void SMTPTransport::startTLS() throw exceptions::command_error("STARTTLS", resp->getText()); ref tlsSession = - vmime::create (getCertificateVerifier()); + tls::TLSSession::create(getCertificateVerifier()); ref tlsSocket = tlsSession->getSocket(m_socket); diff --git a/src/net/tls/TLSSession.cpp b/src/net/tls/TLSSession.cpp index 31149396..44e8a0f4 100644 --- a/src/net/tls/TLSSession.cpp +++ b/src/net/tls/TLSSession.cpp @@ -24,268 +24,19 @@ #include "vmime/config.hpp" -#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT -#include -#if GNUTLS_VERSION_NUMBER < 0x030000 -#include -#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 -# if VMIME_GNUTLS_NEEDS_GCRYPT -# include -# endif -# include -#endif // VMIME_HAVE_PTHREAD - #include "vmime/net/tls/TLSSession.hpp" -#include "vmime/exception.hpp" - - -// Enable GnuTLS debugging by defining GNUTLS_DEBUG -//#define GNUTLS_DEBUG 1 - - -#include -#include - -#if VMIME_DEBUG && GNUTLS_DEBUG - #include -#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 +TLSSession::TLSSession() { - 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 anonCred; - gnutls_certificate_credentials certCred; -}; - -static TLSGlobal g_gnutlsGlobal; - - -#endif // VMIME_BUILDING_DOC - - - -TLSSession::TLSSession(ref cv) - : m_certVerifier(cv) -{ - int res; - - m_gnutlsSession = new gnutls_session; - - 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. -#if VMIME_HAVE_GNUTLS_PRIORITY_FUNCS - gnutls_dh_set_prime_bits(*m_gnutlsSession, 128); - - if ((res = gnutls_priority_set_direct - (*m_gnutlsSession, "NORMAL:%SSL3_RECORD_VERSION", NULL)) != 0) - { - if ((res = gnutls_priority_set_direct - (*m_gnutlsSession, "NORMAL", 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::TLSSession(const TLSSession&) - : object() -{ - // Not used -} - - -TLSSession::~TLSSession() -{ - if (m_gnutlsSession) - { - gnutls_deinit(*m_gnutlsSession); - - delete m_gnutlsSession; - m_gnutlsSession = NULL; - } -} - - -ref TLSSession::getSocket(ref sok) -{ - return vmime::create - (thisRef().dynamicCast (), sok); -} - - -ref TLSSession::getCertificateVerifier() -{ - return m_certVerifier; -} - - -void TLSSession::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()); } @@ -294,5 +45,4 @@ void TLSSession::throwTLSException(const string& fname, const int code) } // vmime -#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS - +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT diff --git a/src/net/tls/TLSSocket.cpp b/src/net/tls/TLSSocket.cpp index 6fa8d02d..266834bf 100644 --- a/src/net/tls/TLSSocket.cpp +++ b/src/net/tls/TLSSocket.cpp @@ -24,18 +24,10 @@ #include "vmime/config.hpp" -#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT -#include -#include - #include "vmime/net/tls/TLSSocket.hpp" -#include "vmime/net/tls/TLSSession.hpp" - -#include "vmime/platform.hpp" - -#include "vmime/security/cert/X509Certificate.hpp" namespace vmime { @@ -43,375 +35,10 @@ namespace net { namespace tls { -TLSSocket::TLSSocket(ref session, ref sok) - : m_session(session), m_wrapped(sok), m_connected(false), - m_handshaking(false), m_ex(NULL) -{ - 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); -} - - -TLSSocket::~TLSSocket() -{ - if (m_ex) - { - delete m_ex; - m_ex = NULL; - } - - try - { - disconnect(); - } - catch (...) - { - // Don't throw exception in destructor - } -} - - -void TLSSocket::connect(const string& address, const port_t port) -{ - m_wrapped->connect(address, port); - - handshake(NULL); - - m_connected = true; -} - - -void TLSSocket::disconnect() -{ - if (m_connected) - { - gnutls_bye(*m_session->m_gnutlsSession, GNUTLS_SHUT_RDWR); - - m_wrapped->disconnect(); - - m_connected = false; - } -} - - -bool TLSSocket::isConnected() const -{ - return m_wrapped->isConnected() && m_connected; -} - - -TLSSocket::size_type TLSSocket::getBlockSize() const -{ - return 16384; // 16 KB -} - - -void TLSSocket::receive(string& buffer) -{ - const int size = receiveRaw(m_buffer, sizeof(m_buffer)); - buffer = vmime::string(m_buffer, size); -} - - -void TLSSocket::send(const string& buffer) -{ - sendRaw(buffer.data(), buffer.length()); -} - - -TLSSocket::size_type TLSSocket::receiveRaw(char* buffer, const size_type count) -{ - const ssize_t ret = gnutls_record_recv - (*m_session->m_gnutlsSession, - buffer, static_cast (count)); - - if (m_ex) - internalThrow(); - - if (ret < 0) - { - if (ret == GNUTLS_E_AGAIN) - return 0; - - TLSSession::throwTLSException("gnutls_record_recv", ret); - } - - return static_cast (ret); -} - - -void TLSSocket::sendRaw(const char* buffer, const size_type count) -{ - gnutls_record_send - (*m_session->m_gnutlsSession, - buffer, static_cast (count)); - - if (m_ex) - internalThrow(); -} - - -void TLSSocket::handshake(ref toHandler) -{ - if (toHandler) - toHandler->resetTimeOut(); - - // Start handshaking process - m_handshaking = true; - m_toHandler = toHandler; - - try - { - while (true) - { - const int ret = gnutls_handshake(*m_session->m_gnutlsSession); - - if (m_ex) - internalThrow(); - - if (ret < 0) - { - if (ret == GNUTLS_E_AGAIN || - ret == GNUTLS_E_INTERRUPTED) - { - // Non-fatal error - platform::getHandler()->wait(); - } - else - { - TLSSession::throwTLSException("gnutls_handshake", ret); - } - } - else - { - // Successful handshake - break; - } - } - } - catch (...) - { - m_handshaking = false; - m_toHandler = NULL; - - throw; - } - - m_handshaking = false; - m_toHandler = NULL; - - // Verify server's certificate(s) - ref certs = getPeerCertificates(); - - if (certs == NULL) - throw exceptions::tls_exception("No peer certificate."); - - m_session->getCertificateVerifier()->verify(certs); - - m_connected = true; -} - - -ssize_t TLSSocket::gnutlsPushFunc - (gnutls_transport_ptr trspt, const void* data, size_t len) -{ - TLSSocket* sok = reinterpret_cast (trspt); - - try - { - sok->m_wrapped->sendRaw - (reinterpret_cast (data), static_cast (len)); - } - catch (exception& e) - { - // Workaround for bad behaviour when throwing C++ exceptions - // from C functions (GNU TLS) - sok->m_ex = e.clone(); - return -1; - } - - return len; -} - - -ssize_t TLSSocket::gnutlsPullFunc - (gnutls_transport_ptr trspt, void* data, size_t len) -{ - TLSSocket* sok = reinterpret_cast (trspt); - - try - { - // Workaround for cross-platform asynchronous handshaking: - // gnutls_handshake() only returns GNUTLS_E_AGAIN if recv() - // returns -1 and errno is set to EGAIN... - if (sok->m_handshaking) - { - while (true) - { - const ssize_t ret = static_cast - (sok->m_wrapped->receiveRaw - (reinterpret_cast (data), - static_cast (len))); - - if (ret == 0) - { - // No data available yet - platform::getHandler()->wait(); - } - else - { - return ret; - } - - // Check whether the time-out delay is elapsed - if (sok->m_toHandler && sok->m_toHandler->isTimeOut()) - { - if (!sok->m_toHandler->handleTimeOut()) - throw exceptions::operation_timed_out(); - - sok->m_toHandler->resetTimeOut(); - } - } - } - else - { - const ssize_t n = static_cast - (sok->m_wrapped->receiveRaw - (reinterpret_cast (data), - static_cast (len))); - - if (n == 0) - return GNUTLS_E_AGAIN; // This seems like a hack, really... - - return n; - } - } - catch (exception& e) - { - // Workaround for bad behaviour when throwing C++ exceptions - // from C functions (GNU TLS) - sok->m_ex = e.clone(); - return -1; - } -} - - -ref TLSSocket::getPeerCertificates() const -{ - unsigned int certCount = 0; - const gnutls_datum* rawData = gnutls_certificate_get_peers - (*m_session->m_gnutlsSession, &certCount); - - if (rawData == NULL) - return NULL; - - // Try X.509 - gnutls_x509_crt* x509Certs = new gnutls_x509_crt[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) - { - // XXX more fine-grained error reporting? - delete [] x509Certs; - return NULL; - } - } - - { - std::vector > 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 data(dataSize); - - gnutls_x509_crt_export(x509Certs[i], - GNUTLS_X509_FMT_DER, &data[0], &dataSize); - - ref 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 vmime::create (certs); - } - - delete [] x509Certs; - - return NULL; -} - - -// 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. - -#ifndef VMIME_BUILDING_DOC - -class TLSSocket_DeleteExWrapper : public object -{ -public: - - TLSSocket_DeleteExWrapper(exception* ex) : m_ex(ex) { } - ~TLSSocket_DeleteExWrapper() { delete m_ex; } - -private: - - exception* m_ex; -}; - -#endif // VMIME_BUILDING_DOC - - -void TLSSocket::internalThrow() -{ - static std::vector > exToDelete; - - if (m_ex) - { - // Reset the current exception pointer to prevent the same - // exception from being thrown again later - exception* ex = m_ex; - m_ex = NULL; - - // To avoid memory leaks - exToDelete.push_back(vmime::create (ex)); - - throw *ex; - } -} - - } // tls } // net } // vmime -#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT diff --git a/src/net/tls/gnutls/TLSSession_GnuTLS.cpp b/src/net/tls/gnutls/TLSSession_GnuTLS.cpp new file mode 100644 index 00000000..644a74ec --- /dev/null +++ b/src/net/tls/gnutls/TLSSession_GnuTLS.cpp @@ -0,0 +1,304 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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 +#if GNUTLS_VERSION_NUMBER < 0x030000 +#include +#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 +# if VMIME_GNUTLS_NEEDS_GCRYPT +# include +# endif +# include +#endif // VMIME_HAVE_PTHREAD + +#include "vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp" +#include "vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp" + +#include "vmime/exception.hpp" + + +// Enable GnuTLS debugging by defining GNUTLS_DEBUG +//#define GNUTLS_DEBUG 1 + + +#include +#include + +#if VMIME_DEBUG && GNUTLS_DEBUG + #include +#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 anonCred; + gnutls_certificate_credentials certCred; +}; + +static TLSGlobal g_gnutlsGlobal; + + +#endif // VMIME_BUILDING_DOC + + + +// static +ref TLSSession::create(ref cv) +{ + return vmime::create (cv); +} + + +TLSSession_GnuTLS::TLSSession_GnuTLS(ref cv) + : m_certVerifier(cv) +{ + int res; + + m_gnutlsSession = new gnutls_session; + + 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. +#if HAVE_GNUTLS_PRIORITY_FUNCS + gnutls_dh_set_prime_bits(*m_gnutlsSession, 128); + + if ((res = gnutls_priority_set_direct + (*m_gnutlsSession, "NORMAL:%SSL3_RECORD_VERSION", NULL)) != 0) + { + if ((res = gnutls_priority_set_direct + (*m_gnutlsSession, "NORMAL", NULL)) != 0) + { + throwTLSException + ("gnutls_priority_set_direct", res); + } + } + +#else // !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 // !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() +{ + if (m_gnutlsSession) + { + gnutls_deinit(*m_gnutlsSession); + + delete m_gnutlsSession; + m_gnutlsSession = NULL; + } +} + + +ref TLSSession_GnuTLS::getSocket(ref sok) +{ + return TLSSocket::wrap(thisRef().dynamicCast (), sok); +} + + +ref 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/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp b/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp new file mode 100644 index 00000000..35e1d415 --- /dev/null +++ b/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp @@ -0,0 +1,424 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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 +#include + +#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" + + +namespace vmime { +namespace net { +namespace tls { + + +// static +ref TLSSocket::wrap(ref session, ref sok) +{ + return vmime::create + (session.dynamicCast (), sok); +} + + +TLSSocket_GnuTLS::TLSSocket_GnuTLS(ref session, ref sok) + : m_session(session), m_wrapped(sok), m_connected(false), + m_handshaking(false), m_ex(NULL) +{ + 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); +} + + +TLSSocket_GnuTLS::~TLSSocket_GnuTLS() +{ + if (m_ex) + { + delete m_ex; + m_ex = NULL; + } + + try + { + disconnect(); + } + catch (...) + { + // Don't throw exception in destructor + } +} + + +void TLSSocket_GnuTLS::connect(const string& address, const port_t port) +{ + m_wrapped->connect(address, port); + + handshake(NULL); + + m_connected = true; +} + + +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; +} + + +TLSSocket::size_type TLSSocket_GnuTLS::getBlockSize() const +{ + return 16384; // 16 KB +} + + +void TLSSocket_GnuTLS::receive(string& buffer) +{ + const int size = receiveRaw(m_buffer, sizeof(m_buffer)); + buffer = vmime::string(m_buffer, size); +} + + +void TLSSocket_GnuTLS::send(const string& buffer) +{ + sendRaw(buffer.data(), buffer.length()); +} + + +TLSSocket::size_type TLSSocket_GnuTLS::receiveRaw(char* buffer, const size_type count) +{ + const ssize_t ret = gnutls_record_recv + (*m_session->m_gnutlsSession, + buffer, static_cast (count)); + + if (m_ex) + internalThrow(); + + if (ret < 0) + { + if (ret == GNUTLS_E_AGAIN) + return 0; + + TLSSession_GnuTLS::throwTLSException("gnutls_record_recv", ret); + } + + return static_cast (ret); +} + + +void TLSSocket_GnuTLS::sendRaw(const char* buffer, const size_type count) +{ + gnutls_record_send + (*m_session->m_gnutlsSession, + buffer, static_cast (count)); + + if (m_ex) + internalThrow(); +} + + +void TLSSocket_GnuTLS::handshake(ref toHandler) +{ + if (toHandler) + toHandler->resetTimeOut(); + + // Start handshaking process + m_handshaking = true; + m_toHandler = toHandler; + + try + { + while (true) + { + const int ret = gnutls_handshake(*m_session->m_gnutlsSession); + + if (m_ex) + internalThrow(); + + if (ret < 0) + { + if (ret == GNUTLS_E_AGAIN || + ret == GNUTLS_E_INTERRUPTED) + { + // Non-fatal error + platform::getHandler()->wait(); + } + else + { + TLSSession_GnuTLS::throwTLSException("gnutls_handshake", ret); + } + } + else + { + // Successful handshake + break; + } + } + } + catch (...) + { + m_handshaking = false; + m_toHandler = NULL; + + throw; + } + + m_handshaking = false; + m_toHandler = NULL; + + // Verify server's certificate(s) + ref certs = getPeerCertificates(); + + if (certs == NULL) + throw exceptions::tls_exception("No peer certificate."); + + m_session->getCertificateVerifier()->verify(certs); + + m_connected = true; +} + + +ssize_t TLSSocket_GnuTLS::gnutlsPushFunc + (gnutls_transport_ptr trspt, const void* data, size_t len) +{ + TLSSocket_GnuTLS* sok = reinterpret_cast (trspt); + + try + { + sok->m_wrapped->sendRaw + (reinterpret_cast (data), static_cast (len)); + } + catch (exception& e) + { + // Workaround for bad behaviour when throwing C++ exceptions + // from C functions (GNU TLS) + sok->m_ex = e.clone(); + return -1; + } + + return len; +} + + +ssize_t TLSSocket_GnuTLS::gnutlsPullFunc + (gnutls_transport_ptr trspt, void* data, size_t len) +{ + TLSSocket_GnuTLS* sok = reinterpret_cast (trspt); + + try + { + // Workaround for cross-platform asynchronous handshaking: + // gnutls_handshake() only returns GNUTLS_E_AGAIN if recv() + // returns -1 and errno is set to EGAIN... + if (sok->m_handshaking) + { + while (true) + { + const ssize_t ret = static_cast + (sok->m_wrapped->receiveRaw + (reinterpret_cast (data), + static_cast (len))); + + if (ret == 0) + { + // No data available yet + platform::getHandler()->wait(); + } + else + { + return ret; + } + + // Check whether the time-out delay is elapsed + if (sok->m_toHandler && sok->m_toHandler->isTimeOut()) + { + if (!sok->m_toHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + + sok->m_toHandler->resetTimeOut(); + } + } + } + else + { + const ssize_t n = static_cast + (sok->m_wrapped->receiveRaw + (reinterpret_cast (data), + static_cast (len))); + + if (n == 0) + return GNUTLS_E_AGAIN; // This seems like a hack, really... + + return n; + } + } + catch (exception& e) + { + // Workaround for bad behaviour when throwing C++ exceptions + // from C functions (GNU TLS) + sok->m_ex = e.clone(); + return -1; + } +} + + +ref TLSSocket_GnuTLS::getPeerCertificates() const +{ + unsigned int certCount = 0; + const gnutls_datum* rawData = gnutls_certificate_get_peers + (*m_session->m_gnutlsSession, &certCount); + + if (rawData == NULL) + return NULL; + + // Try X.509 + gnutls_x509_crt* x509Certs = new gnutls_x509_crt[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) + { + // XXX more fine-grained error reporting? + delete [] x509Certs; + return NULL; + } + } + + { + std::vector > 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 data(dataSize); + + gnutls_x509_crt_export(x509Certs[i], + GNUTLS_X509_FMT_DER, &data[0], &dataSize); + + ref 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 vmime::create (certs); + } + + delete [] x509Certs; + + return NULL; +} + + +// 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. + +#ifndef VMIME_BUILDING_DOC + +class TLSSocket_DeleteExWrapper : public object +{ +public: + + TLSSocket_DeleteExWrapper(exception* ex) : m_ex(ex) { } + ~TLSSocket_DeleteExWrapper() { delete m_ex; } + +private: + + exception* m_ex; +}; + +#endif // VMIME_BUILDING_DOC + + +void TLSSocket_GnuTLS::internalThrow() +{ + static std::vector > exToDelete; + + if (m_ex) + { + // Reset the current exception pointer to prevent the same + // exception from being thrown again later + exception* ex = m_ex; + m_ex = NULL; + + // To avoid memory leaks + exToDelete.push_back(vmime::create (ex)); + + throw *ex; + } +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS diff --git a/src/net/tls/openssl/OpenSSLInitializer.cpp b/src/net/tls/openssl/OpenSSLInitializer.cpp new file mode 100755 index 00000000..ec23ceb4 --- /dev/null +++ b/src/net/tls/openssl/OpenSSLInitializer.cpp @@ -0,0 +1,152 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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/utility/sync/autoLock.hpp" +#include "vmime/utility/sync/criticalSection.hpp" + +#include "vmime/platform.hpp" + +#include +#include +#include +#include + +#if OPENSSL_VERSION_NUMBER >= 0x0907000L +# include +#endif + + +namespace vmime { +namespace net { +namespace tls { + + +ref * OpenSSLInitializer::sm_mutexes; +int OpenSSLInitializer::sm_initCount(0); + + +OpenSSLInitializer::OpenSSLInitializer() +{ + initialize(); +} + + +OpenSSLInitializer::~OpenSSLInitializer() +{ + uninitialize(); +} + + +// static +ref OpenSSLInitializer::getMutex() +{ + static ref criticalSection + = vmime::platform::getHandler()->createCriticalSection(); + + return criticalSection; +} + + +// static +void OpenSSLInitializer::initialize() +{ + ref mutex = getMutex(); + vmime::utility::sync::autoLock lock(mutex); + + if (++sm_initCount == 1) + { +#if OPENSSL_VERSION_NUMBER >= 0x0907000L + OPENSSL_config(NULL); +#endif + + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + + unsigned char seed[SEEDSIZE]; + vmime::platform::getHandler()->generateRandomBytes(seed, SEEDSIZE); + RAND_seed(seed, SEEDSIZE); + + int numMutexes = CRYPTO_num_locks(); + sm_mutexes = new ref [numMutexes]; + + for (int i = 0 ; i < numMutexes ; ++i) + sm_mutexes[i] = vmime::platform::getHandler()->createCriticalSection(); + + CRYPTO_set_locking_callback(&OpenSSLInitializer::lock); + CRYPTO_set_id_callback(&OpenSSLInitializer::id); + } +} + + +// static +void OpenSSLInitializer::uninitialize() +{ + ref mutex = getMutex(); + vmime::utility::sync::autoLock lock(mutex); + + if (--sm_initCount == 0) + { + EVP_cleanup(); + ERR_free_strings(); + + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + + delete [] sm_mutexes; + } +} + + +// static +void OpenSSLInitializer::lock(int mode, int n, const char* /* file */, int /* line */) +{ + if (mode & CRYPTO_LOCK) + sm_mutexes[n]->lock(); + else + sm_mutexes[n]->unlock(); +} + + +// static +unsigned long OpenSSLInitializer::id() +{ + return vmime::platform::getHandler()->getThreadId(); +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + diff --git a/src/net/tls/openssl/TLSSession_OpenSSL.cpp b/src/net/tls/openssl/TLSSession_OpenSSL.cpp new file mode 100755 index 00000000..e03c90f6 --- /dev/null +++ b/src/net/tls/openssl/TLSSession_OpenSSL.cpp @@ -0,0 +1,138 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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 + + +#if VMIME_HAVE_PTHREAD +# include +# include +# include +#endif // VMIME_HAVE_PTHREAD + +#include "vmime/net/tls/openssl/TLSSession_OpenSSL.hpp" +#include "vmime/net/tls/openssl/OpenSSLInitializer.hpp" + +#include "vmime/exception.hpp" + +#include +#include + + +namespace vmime { +namespace net { +namespace tls { + + +// Thread-safe OpenSSL initialization +static OpenSSLInitializer g_openSSLGlobal; + + +// static +ref TLSSession::create(ref cv) +{ + return vmime::create (cv); +} + + +TLSSession_OpenSSL::TLSSession_OpenSSL(ref cv) + : m_sslctx(0), m_certVerifier(cv) +{ + m_sslctx = SSL_CTX_new(SSLv23_client_method()); + SSL_CTX_set_options(m_sslctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); +} + + +TLSSession_OpenSSL::TLSSession_OpenSSL(const TLSSession_OpenSSL&) + : TLSSession() +{ + // Not used +} + + +TLSSession_OpenSSL::~TLSSession_OpenSSL() +{ + SSL_CTX_free(m_sslctx); +} + + +ref TLSSession_OpenSSL::getSocket(ref sok) +{ + return TLSSocket::wrap(thisRef().dynamicCast (), sok); +} + + +ref TLSSession_OpenSSL::getCertificateVerifier() +{ + return m_certVerifier; +} + + +void TLSSession_OpenSSL::usePrivateKeyFile(const vmime::string& keyfile) +{ + 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 exceptions::certificate_exception(oss.str()); + } +} + + +void TLSSession_OpenSSL::useCertificateChainFile(const vmime::string& chainFile) +{ + 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 exceptions::certificate_exception(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/src/net/tls/openssl/TLSSocket_OpenSSL.cpp b/src/net/tls/openssl/TLSSocket_OpenSSL.cpp new file mode 100755 index 00000000..cbd1f9fd --- /dev/null +++ b/src/net/tls/openssl/TLSSocket_OpenSSL.cpp @@ -0,0 +1,416 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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 +#include + +#include "vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp" +#include "vmime/net/tls/openssl/TLSSession_OpenSSL.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp" + +#include + + +namespace vmime { +namespace net { +namespace tls { + + +// static +ref TLSSocket::wrap(ref session, ref sok) +{ + return vmime::create + (session.dynamicCast (), sok); +} + + +TLSSocket_OpenSSL::TLSSocket_OpenSSL(ref session, ref sok) + : m_session(session), m_wrapped(sok), m_connected(false), m_ssl(0) +{ +} + + +TLSSocket_OpenSSL::~TLSSocket_OpenSSL() +{ + try + { + disconnect(); + + if (m_ssl) + { + SSL_free(m_ssl); + m_ssl = 0; + } + } + catch (...) + { + // Don't throw in destructor + } +} + + +void TLSSocket_OpenSSL::createSSLHandle() +{ + if (m_wrapped->isConnected()) + { + static BIO_METHOD customBIOMethod; + ::memset(&customBIOMethod, 0, sizeof(customBIOMethod)); + + customBIOMethod.type = 100 | BIO_TYPE_SOURCE_SINK; + customBIOMethod.name = "vmime::socket glue"; + customBIOMethod.bwrite = bio_write; + customBIOMethod.bread = bio_read; + customBIOMethod.bputs = bio_puts; + customBIOMethod.bgets = bio_gets; + customBIOMethod.ctrl = bio_ctrl; + customBIOMethod.create = bio_create; + customBIOMethod.destroy = bio_destroy; + + BIO* sockBio = BIO_new(&customBIOMethod); + sockBio->ptr = this; + + 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_connect_state(m_ssl); + } + else + { + throw exceptions::tls_exception("Unconnected socket error"); + } +} + + +void TLSSocket_OpenSSL::connect(const string& address, const port_t port) +{ + m_wrapped->connect(address, port); + + createSSLHandle(); + + handshake(NULL); + + m_connected = true; +} + + +void TLSSocket_OpenSSL::disconnect() +{ + if (m_connected) + { + 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); + } + + m_wrapped->disconnect(); + m_connected = false; + } +} + + +bool TLSSocket_OpenSSL::isConnected() const +{ + return m_wrapped->isConnected() && m_connected; +} + + +TLSSocket::size_type TLSSocket_OpenSSL::getBlockSize() const +{ + return 16384; // 16 KB +} + + +void TLSSocket_OpenSSL::receive(string& buffer) +{ + const int size = receiveRaw(m_buffer, sizeof(m_buffer)); + buffer = vmime::string(m_buffer, size); +} + + +void TLSSocket_OpenSSL::send(const string& buffer) +{ + sendRaw(buffer.data(), buffer.length()); +} + + +TLSSocket::size_type TLSSocket_OpenSSL::receiveRaw(char* buffer, const size_type count) +{ + int rc = SSL_read(m_ssl, buffer, count); + handleError(rc); + return rc; +} + + +void TLSSocket_OpenSSL::sendRaw(const char* buffer, const size_type count) +{ + int rc = SSL_write(m_ssl, buffer, count); + handleError(rc); +} + + +void TLSSocket_OpenSSL::handshake(ref toHandler) +{ + if (toHandler) + toHandler->resetTimeOut(); + + // Start handshaking process + m_toHandler = toHandler; + + if (!m_ssl) + createSSLHandle(); + + try + { + // int ret = SSL_connect(m_ssl); + int ret = SSL_do_handshake(m_ssl); + handleError(ret); + } + catch (...) + { + SSL_free(m_ssl); + m_ssl = 0; + m_toHandler = NULL; + throw; + } + + m_toHandler = NULL; + + // Verify server's certificate(s) + ref certs = getPeerCertificates(); + + if (certs == NULL) + throw exceptions::tls_exception("No peer certificate."); + + m_session->getCertificateVerifier()->verify(certs); + + m_connected = true; +} + + +ref TLSSocket_OpenSSL::getPeerCertificates() const +{ + 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 > certs; + + for (int i = 0; i < certCount && !error; i++) + { + ref 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 vmime::create (certs); +} + + +void TLSSocket_OpenSSL::handleError(int rc) +{ + if (rc > 0) return; + + int sslError = SSL_get_error(m_ssl, rc); + long lastError = ERR_get_error(); + + switch (sslError) + { + case SSL_ERROR_ZERO_RETURN: + return; + + case SSL_ERROR_SYSCALL: + { + if (lastError == 0) + { + if (rc == 0) + throw exceptions::tls_exception("SSL connection unexpectedly closed"); + else + { + vmime::string msg; + std::ostringstream oss(msg); + oss << "The BIO reported an error: %d" << rc; + oss.flush(); + throw exceptions::tls_exception(oss.str()); + } + } + break; + } + + //// Follwoing errors should not occur + // With SSL_MODE_AUTO_RETRY these should not happen + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + // 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; + } +} + + +// Implementation of custom BIO methods + + +// static +int TLSSocket_OpenSSL::bio_write(BIO* bio, const char* buf, int len) +{ + if (buf == NULL || len <= 0) + return 0; + + TLSSocket_OpenSSL *sok = reinterpret_cast (bio->ptr); + + sok->m_wrapped->sendRaw(buf, len); + + return len; +} + + +// static +int TLSSocket_OpenSSL::bio_read(BIO* bio, char* buf, int len) +{ + if (buf == NULL || len <= 0) + return 0; + + TLSSocket_OpenSSL *sok = reinterpret_cast (bio->ptr); + + const int n = sok->m_wrapped->receiveRaw(buf, len); + + if (n == 0) + BIO_set_retry_read(bio); // This seems like a hack, really... + else + BIO_clear_retry_flags(bio); + + return n; +} + + +// static +int TLSSocket_OpenSSL::bio_puts(BIO* bio, const char* str) +{ + return bio_write(bio, str, strlen(str)); +} + + +// static +int TLSSocket_OpenSSL::bio_gets(BIO* /* bio */, char* /* buf */, int /* len */) +{ + return -1; +} + + +// static +long TLSSocket_OpenSSL::bio_ctrl(BIO* /* bio */, int cmd, long /* num */, void* /* ptr */) +{ + if (cmd == BIO_CTRL_FLUSH) + { + // OpenSSL library needs this + return 1; + } + + return 0; +} + + +// static +int TLSSocket_OpenSSL::bio_create(BIO* bio) +{ + bio->init = 1; + bio->num = 0; + bio->ptr = NULL; + bio->flags = 0; + + return 1; +} + + +// static +int TLSSocket_OpenSSL::bio_destroy(BIO* bio) +{ + if (bio == NULL) + return 0; + + bio->ptr = NULL; + bio->init = 0; + bio->flags = 0; + + return 1; +} + + +} // tls +} // net +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL diff --git a/src/platforms/posix/posixCriticalSection.cpp b/src/platforms/posix/posixCriticalSection.cpp new file mode 100755 index 00000000..83649fc0 --- /dev/null +++ b/src/platforms/posix/posixCriticalSection.cpp @@ -0,0 +1,67 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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_PLATFORM_IS_POSIX + + +#include "vmime/platforms/posix/posixCriticalSection.hpp" + + +namespace vmime { +namespace platforms { +namespace posix { + + +posixCriticalSection::posixCriticalSection() +{ + pthread_mutex_init(&m_cs, NULL); +} + + +posixCriticalSection::~posixCriticalSection() +{ + pthread_mutex_destroy(&m_cs); +} + + +void posixCriticalSection::lock() +{ + pthread_mutex_lock(&m_cs); +} + + +void posixCriticalSection::unlock() +{ + pthread_mutex_unlock(&m_cs); +} + + +} // posix +} // platforms +} // vmime + + +#endif // VMIME_PLATFORM_IS_POSIX diff --git a/src/platforms/posix/posixHandler.cpp b/src/platforms/posix/posixHandler.cpp index 8629b1e2..50428b70 100644 --- a/src/platforms/posix/posixHandler.cpp +++ b/src/platforms/posix/posixHandler.cpp @@ -29,8 +29,10 @@ #include "vmime/platforms/posix/posixHandler.hpp" -#include +#include "vmime/platforms/posix/posixCriticalSection.hpp" +#include +#include #include #include #include @@ -39,10 +41,15 @@ #include #include +#if VMIME_HAVE_SYSCALL +# include +#endif + #include #include #include +#include #if VMIME_HAVE_PTHREAD # include @@ -217,6 +224,18 @@ unsigned int posixHandler::getProcessId() const } +unsigned int posixHandler::getThreadId() const +{ +#if VMIME_HAVE_GETTID + return static_cast (::gettid()); +#elif VMIME_HAVE_SYSCALL && VMIME_HAVE_SYSCALL_GETTID + return static_cast (::syscall(SYS_gettid)); +#else + #error We have no implementation of getThreadId() for this platform! +#endif +} + + #if VMIME_HAVE_MESSAGING_FEATURES ref posixHandler::getSocketFactory() @@ -261,6 +280,29 @@ void posixHandler::wait() const } +void posixHandler::generateRandomBytes(unsigned char* buffer, const unsigned int count) +{ + int fd = open("/dev/urandom", O_RDONLY); + + if (fd != -1) + { + read(fd, buffer, count); + close(fd); + } + else // fallback + { + for (unsigned int i = 0 ; i < count ; ++i) + buffer[i] = static_cast (rand() % 255); + } +} + + +ref posixHandler::createCriticalSection() +{ + return vmime::create (); +} + + } // posix } // platforms } // vmime diff --git a/src/platforms/windows/windowsCriticalSection.cpp b/src/platforms/windows/windowsCriticalSection.cpp new file mode 100755 index 00000000..d3aebd09 --- /dev/null +++ b/src/platforms/windows/windowsCriticalSection.cpp @@ -0,0 +1,67 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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_PLATFORM_IS_WINDOWS + + +#include "vmime/platforms/windows/windowsCriticalSection.hpp" + + +namespace vmime { +namespace platforms { +namespace windows { + + +windowsCriticalSection::windowsCriticalSection() +{ + InitializeCriticalSectionAndSpinCount(&m_cs, 0x400); +} + + +windowsCriticalSection::~windowsCriticalSection() +{ + DeleteCriticalSection(&m_cs) +} + + +void windowsCriticalSection::lock() +{ + EnterCriticalSection(&m_cs); +} + + +void windowsCriticalSection::unlock() +{ + LeaveCriticalSection(&m_cs); +} + + +} // windows +} // platforms +} // vmime + + +#endif // VMIME_PLATFORM_IS_WINDOWS diff --git a/src/platforms/windows/windowsHandler.cpp b/src/platforms/windows/windowsHandler.cpp index 1ed4025b..824589b7 100644 --- a/src/platforms/windows/windowsHandler.cpp +++ b/src/platforms/windows/windowsHandler.cpp @@ -29,6 +29,8 @@ #include "vmime/platforms/windows/windowsHandler.hpp" +#include "vmime/platforms/windows/windowsCriticalSection.hpp" + #include #include #include @@ -239,6 +241,12 @@ unsigned int windowsHandler::getProcessId() const } +unsigned int windowsHandler::getThreadId() cont +{ + return static_cast (::GetCurrentThreadId()); +} + + #if VMIME_HAVE_MESSAGING_FEATURES ref windowsHandler::getSocketFactory() @@ -272,6 +280,21 @@ void windowsHandler::wait() const } +void windowsHandler::generateRandomBytes(unsigned char* buffer, const unsigned int count) +{ + HCRYPTPROV cryptProvider = 0; + CryptAcquireContext(&cryptProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + CryptGenRandom(cryptProvider, static_cast (count), static_cast (buffer)); + CryptReleaseContext(cryptProvider, 0); +} + + +ref posixHandler::createCriticalSection() +{ + return vmime::create (); +} + + } // posix } // platforms } // vmime diff --git a/src/security/cert/X509Certificate.cpp b/src/security/cert/X509Certificate.cpp index 8df4e5e8..4b177f13 100644 --- a/src/security/cert/X509Certificate.cpp +++ b/src/security/cert/X509Certificate.cpp @@ -21,240 +21,22 @@ // the GNU General Public License cover the whole combination. // -#include -#include +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT -#include #include "vmime/security/cert/X509Certificate.hpp" -#include "vmime/utility/outputStreamByteArrayAdapter.hpp" - namespace vmime { namespace security { namespace cert { -#ifndef VMIME_BUILDING_DOC - -struct X509CertificateInternalData -{ - X509CertificateInternalData() - { - gnutls_x509_crt_init(&cert); - } - - ~X509CertificateInternalData() - { - gnutls_x509_crt_deinit(cert); - } - - - gnutls_x509_crt cert; -}; - -#endif // VMIME_BUILDING_DOC - - -X509Certificate::X509Certificate() - : m_data(new X509CertificateInternalData) -{ -} - - -X509Certificate::X509Certificate(const X509Certificate&) - : certificate(), m_data(NULL) -{ - // Not used -} - - X509Certificate::~X509Certificate() { - delete m_data; -} - - -// static -ref X509Certificate::import(utility::inputStream& is) -{ - byteArray bytes; - utility::stream::value_type chunk[4096]; - - while (!is.eof()) - { - const int len = is.read(chunk, sizeof(chunk)); - bytes.insert(bytes.end(), chunk, chunk + len); - } - - return import(&bytes[0], bytes.size()); -} - - -// static -ref X509Certificate::import - (const byte_t* data, const unsigned int length) -{ - gnutls_datum buffer; - buffer.data = const_cast (data); - buffer.size = length; - - // Try DER format - ref derCert = vmime::create (); - - if (gnutls_x509_crt_import(derCert->m_data->cert, &buffer, GNUTLS_X509_FMT_DER) >= 0) - return derCert; - - // Try PEM format - ref pemCert = vmime::create (); - - if (gnutls_x509_crt_import(pemCert->m_data->cert, &buffer, GNUTLS_X509_FMT_PEM) >= 0) - return pemCert; - - return NULL; -} - - -void X509Certificate::write - (utility::outputStream& os, const Format format) const -{ - size_t dataSize = 0; - gnutls_x509_crt_fmt fmt = GNUTLS_X509_FMT_DER; - - switch (format) - { - case FORMAT_DER: fmt = GNUTLS_X509_FMT_DER; break; - case FORMAT_PEM: fmt = GNUTLS_X509_FMT_PEM; break; - } - - gnutls_x509_crt_export(m_data->cert, fmt, NULL, &dataSize); - - std::vector data(dataSize); - - gnutls_x509_crt_export(m_data->cert, fmt, &data[0], &dataSize); - - os.write(reinterpret_cast (&data[0]), dataSize); -} - - -const byteArray X509Certificate::getSerialNumber() const -{ - char serial[64]; - size_t serialSize = sizeof(serial); - - gnutls_x509_crt_get_serial(m_data->cert, serial, &serialSize); - - return byteArray(serial, serial + serialSize); -} - - -bool X509Certificate::checkIssuer(ref issuer) const -{ - return (gnutls_x509_crt_check_issuer - (m_data->cert, issuer->m_data->cert) >= 1); -} - - -bool X509Certificate::verify(ref caCert) const -{ - unsigned int verify = 0; - - const int res = gnutls_x509_crt_verify - (m_data->cert, &(caCert->m_data->cert), 1, - GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, - &verify); - - return (res == 0 && verify == 0); -} - - -const datetime X509Certificate::getActivationDate() const -{ - const time_t t = gnutls_x509_crt_get_activation_time(m_data->cert); - return datetime(t); -} - - -const datetime X509Certificate::getExpirationDate() const -{ - const time_t t = gnutls_x509_crt_get_expiration_time(m_data->cert); - return datetime(t); -} - - -const byteArray X509Certificate::getFingerprint(const DigestAlgorithm algo) const -{ - gnutls_digest_algorithm galgo; - - switch (algo) - { - case DIGEST_MD5: - - galgo = GNUTLS_DIG_MD5; - break; - - default: - case DIGEST_SHA1: - - galgo = GNUTLS_DIG_SHA; - break; - } - - size_t bufferSize = 0; - gnutls_x509_crt_get_fingerprint - (m_data->cert, galgo, NULL, &bufferSize); - - std::vector buffer(bufferSize); - - if (gnutls_x509_crt_get_fingerprint - (m_data->cert, galgo, &buffer[0], &bufferSize) == 0) - { - byteArray res; - res.insert(res.end(), &buffer[0], &buffer[0] + bufferSize); - - return res; - } - - return byteArray(); -} - - -const byteArray X509Certificate::getEncoded() const -{ - byteArray bytes; - utility::outputStreamByteArrayAdapter os(bytes); - - write(os, FORMAT_DER); - - return bytes; -} - - -const string X509Certificate::getType() const -{ - return "X.509"; -} - - -int X509Certificate::getVersion() const -{ - return gnutls_x509_crt_get_version(m_data->cert); -} - - -bool X509Certificate::equals(ref other) const -{ - ref otherX509 = - other.dynamicCast (); - - if (!otherX509) - return false; - - const byteArray fp1 = getFingerprint(DIGEST_MD5); - const byteArray fp2 = otherX509->getFingerprint(DIGEST_MD5); - - return fp1 == fp2; } @@ -262,3 +44,5 @@ bool X509Certificate::equals(ref other) const } // security } // vmime + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT diff --git a/src/security/cert/gnutls/X509Certificate_GnuTLS.cpp b/src/security/cert/gnutls/X509Certificate_GnuTLS.cpp new file mode 100644 index 00000000..739b03f1 --- /dev/null +++ b/src/security/cert/gnutls/X509Certificate_GnuTLS.cpp @@ -0,0 +1,278 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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 +#include + +#include + +#include "vmime/security/cert/gnutls/X509Certificate_GnuTLS.hpp" + +#include "vmime/utility/outputStreamByteArrayAdapter.hpp" + + +namespace vmime { +namespace security { +namespace cert { + + +#ifndef VMIME_BUILDING_DOC + +struct GnuTLSX509CertificateInternalData +{ + GnuTLSX509CertificateInternalData() + { + gnutls_x509_crt_init(&cert); + } + + ~GnuTLSX509CertificateInternalData() + { + gnutls_x509_crt_deinit(cert); + } + + + gnutls_x509_crt cert; +}; + +#endif // VMIME_BUILDING_DOC + + +X509Certificate_GnuTLS::X509Certificate_GnuTLS() + : m_data(new GnuTLSX509CertificateInternalData) +{ +} + + +X509Certificate_GnuTLS::X509Certificate_GnuTLS(const X509Certificate&) + : certificate(), m_data(NULL) +{ + // Not used +} + + +X509Certificate_GnuTLS::~X509Certificate_GnuTLS() +{ + delete m_data; +} + + +// static +ref X509Certificate::import(utility::inputStream& is) +{ + byteArray bytes; + utility::stream::value_type chunk[4096]; + + while (!is.eof()) + { + const int len = is.read(chunk, sizeof(chunk)); + bytes.insert(bytes.end(), chunk, chunk + len); + } + + return import(&bytes[0], bytes.size()); +} + + +// static +ref X509Certificate::import + (const byte_t* data, const unsigned int length) +{ + gnutls_datum buffer; + buffer.data = const_cast (data); + buffer.size = length; + + // Try DER format + ref derCert = vmime::create (); + + if (gnutls_x509_crt_import(derCert->m_data->cert, &buffer, GNUTLS_X509_FMT_DER) >= 0) + return derCert; + + // Try PEM format + ref pemCert = vmime::create (); + + if (gnutls_x509_crt_import(pemCert->m_data->cert, &buffer, GNUTLS_X509_FMT_PEM) >= 0) + return pemCert; + + return NULL; +} + + +void X509Certificate_GnuTLS::write + (utility::outputStream& os, const Format format) const +{ + size_t dataSize = 0; + gnutls_x509_crt_fmt fmt = GNUTLS_X509_FMT_DER; + + switch (format) + { + case FORMAT_DER: fmt = GNUTLS_X509_FMT_DER; break; + case FORMAT_PEM: fmt = GNUTLS_X509_FMT_PEM; break; + } + + gnutls_x509_crt_export(m_data->cert, fmt, NULL, &dataSize); + + std::vector data(dataSize); + + gnutls_x509_crt_export(m_data->cert, fmt, &data[0], &dataSize); + + os.write(reinterpret_cast (&data[0]), dataSize); +} + + +const byteArray X509Certificate_GnuTLS::getSerialNumber() const +{ + char serial[64]; + size_t serialSize = sizeof(serial); + + gnutls_x509_crt_get_serial(m_data->cert, serial, &serialSize); + + return byteArray(serial, serial + serialSize); +} + + +bool X509Certificate_GnuTLS::checkIssuer(ref issuer_) const +{ + ref issuer = + issuer.dynamicCast (); + + return (gnutls_x509_crt_check_issuer + (m_data->cert, issuer->m_data->cert) >= 1); +} + + +bool X509Certificate_GnuTLS::verify(ref caCert_) const +{ + ref caCert = + caCert_.dynamicCast (); + + unsigned int verify = 0; + + const int res = gnutls_x509_crt_verify + (m_data->cert, &(caCert->m_data->cert), 1, + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, + &verify); + + return (res == 0 && verify == 0); +} + + +const datetime X509Certificate_GnuTLS::getActivationDate() const +{ + const time_t t = gnutls_x509_crt_get_activation_time(m_data->cert); + return datetime(t); +} + + +const datetime X509Certificate_GnuTLS::getExpirationDate() const +{ + const time_t t = gnutls_x509_crt_get_expiration_time(m_data->cert); + return datetime(t); +} + + +const byteArray X509Certificate_GnuTLS::getFingerprint(const DigestAlgorithm algo) const +{ + gnutls_digest_algorithm galgo; + + switch (algo) + { + case DIGEST_MD5: + + galgo = GNUTLS_DIG_MD5; + break; + + default: + case DIGEST_SHA1: + + galgo = GNUTLS_DIG_SHA; + break; + } + + size_t bufferSize = 0; + gnutls_x509_crt_get_fingerprint + (m_data->cert, galgo, NULL, &bufferSize); + + std::vector buffer(bufferSize); + + if (gnutls_x509_crt_get_fingerprint + (m_data->cert, galgo, &buffer[0], &bufferSize) == 0) + { + byteArray res; + res.insert(res.end(), &buffer[0], &buffer[0] + bufferSize); + + return res; + } + + return byteArray(); +} + + +const byteArray X509Certificate_GnuTLS::getEncoded() const +{ + byteArray bytes; + utility::outputStreamByteArrayAdapter os(bytes); + + write(os, FORMAT_DER); + + return bytes; +} + + +const string X509Certificate_GnuTLS::getType() const +{ + return "X.509"; +} + + +int X509Certificate_GnuTLS::getVersion() const +{ + return gnutls_x509_crt_get_version(m_data->cert); +} + + +bool X509Certificate_GnuTLS::equals(ref other) const +{ + ref otherX509 = + other.dynamicCast (); + + if (!otherX509) + return false; + + const byteArray fp1 = getFingerprint(DIGEST_MD5); + const byteArray fp2 = otherX509->getFingerprint(DIGEST_MD5); + + return fp1 == fp2; +} + + +} // cert +} // security +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS diff --git a/src/security/cert/openssl/X509Certificate_OpenSSL.cpp b/src/security/cert/openssl/X509Certificate_OpenSSL.cpp new file mode 100755 index 00000000..dacb006e --- /dev/null +++ b/src/security/cert/openssl/X509Certificate_OpenSSL.cpp @@ -0,0 +1,467 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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 +#include +#include +#include + +#include "vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp" + +#include "vmime/utility/outputStreamByteArrayAdapter.hpp" + +#include "vmime/exception.hpp" + +#include +#include +#include +#include + + +namespace vmime { +namespace security { +namespace cert { + + +#ifndef VMIME_BUILDING_DOC + +class monthMap +{ +public: + + monthMap() + { + m_monthMap["jan"] = vmime::datetime::JAN; + m_monthMap["feb"] = vmime::datetime::FEB; + m_monthMap["mar"] = vmime::datetime::MAR; + m_monthMap["apr"] = vmime::datetime::APR; + m_monthMap["may"] = vmime::datetime::MAY; + m_monthMap["jun"] = vmime::datetime::JUN; + m_monthMap["jul"] = vmime::datetime::JUL; + m_monthMap["aug"] = vmime::datetime::AUG; + m_monthMap["sep"] = vmime::datetime::SEP; + m_monthMap["oct"] = vmime::datetime::OCT; + m_monthMap["nov"] = vmime::datetime::NOV; + m_monthMap["dec"] = vmime::datetime::DEC; + } + + int getMonth(vmime::string mstr) + { + std::transform(mstr.begin(), mstr.end(), mstr.begin(), ::tolower); + + std::map ::const_iterator + c_it = m_monthMap.find(mstr); + + if (c_it != m_monthMap.end()) + return c_it->second; + + return -1; + } + +private: + + std::map m_monthMap; +}; + +static monthMap sg_monthMap; + + + +struct OpenSSLX509CertificateInternalData +{ + OpenSSLX509CertificateInternalData() + { + cert = 0; + } + + ~OpenSSLX509CertificateInternalData() + { + if (cert) + X509_free(cert); + } + + X509* cert; +}; + +#endif // VMIME_BUILDING_DOC + + +X509Certificate_OpenSSL::X509Certificate_OpenSSL() + : m_data(new OpenSSLX509CertificateInternalData) +{ +} + + +X509Certificate_OpenSSL::X509Certificate_OpenSSL(X509* cert) + : m_data(new OpenSSLX509CertificateInternalData) +{ + m_data->cert = X509_dup(cert); +} + + +X509Certificate_OpenSSL::X509Certificate_OpenSSL(const X509Certificate_OpenSSL&) + : X509Certificate(), m_data(NULL) +{ + // Not used +} + + +X509Certificate_OpenSSL::~X509Certificate_OpenSSL() +{ + delete m_data; +} + + +// static +ref X509Certificate_OpenSSL::importInternal(X509* cert) +{ + if (cert) + return vmime::create (reinterpret_cast (cert)); + + return NULL; +} + + +// static +ref X509Certificate::import(utility::inputStream& is) +{ + byteArray bytes; + utility::stream::value_type chunk[4096]; + + while (!is.eof()) + { + const int len = is.read(chunk, sizeof(chunk)); + bytes.insert(bytes.end(), chunk, chunk + len); + } + + return import(&bytes[0], bytes.size()); +} + + +// static +ref X509Certificate::import + (const byte_t* data, const unsigned int length) +{ + ref cert = vmime::create (); + + BIO* membio = BIO_new_mem_buf(const_cast (data), length); + + if (!PEM_read_bio_X509(membio, &(cert->m_data->cert), 0, 0)) + { + BIO_vfree(membio); + return NULL; + } + + BIO_vfree(membio); + + return cert; +} + + +void X509Certificate_OpenSSL::write + (utility::outputStream& os, const Format format) const +{ + BIO* membio = 0; + int dataSize = 0; + unsigned char* out = 0; + + if (format == FORMAT_DER) + { + if ((dataSize = i2d_X509(m_data->cert, &out)) < 0) + goto err; + + os.write(reinterpret_cast (out), dataSize); + os.flush(); + OPENSSL_free(out); + } + else if (format == FORMAT_PEM) + { + membio = BIO_new(BIO_s_mem()); + BIO_set_close(membio, BIO_CLOSE); + + if (!PEM_write_bio_X509(membio, m_data->cert)) + goto pem_err; + + dataSize = BIO_get_mem_data(membio, &out); + os.write(reinterpret_cast (out), dataSize); + os.flush(); + BIO_vfree(membio); + } + else + { + throw vmime::exceptions::unsupported_certificate_type("Unknown cert type"); + } + + return; // #### Early Return #### + +pem_err: + { + if (membio) + BIO_vfree(membio); + } + +err: + { + char errstr[256]; + long ec = ERR_get_error(); + ERR_error_string(ec, errstr); + throw vmime::exceptions::certificate_exception( + "OpenSSLX509Certificate_OpenSSL::write exception - " + string(errstr)); + } +} + + +const byteArray X509Certificate_OpenSSL::getSerialNumber() const +{ + ASN1_INTEGER *serial = X509_get_serialNumber(m_data->cert); + BIGNUM *bnser = ASN1_INTEGER_to_BN(serial, NULL); + int n = BN_num_bytes(bnser); + byte_t* outbuf = new byte_t[n]; + BN_bn2bin(bnser, outbuf); + byteArray ser(outbuf, outbuf + n); + delete [] outbuf; + BN_free(bnser); + return ser; +} + + +bool X509Certificate_OpenSSL::checkIssuer(ref cert_) const +{ + ref cert = + cert_.dynamicCast (); + + // Get issuer for this cert + BIO *out; + unsigned char *issuer; + + out = BIO_new(BIO_s_mem()); + X509_NAME_print_ex(out, X509_get_issuer_name(m_data->cert), 0, XN_FLAG_RFC2253); + int n = BIO_get_mem_data(out, &issuer); + vmime::string thisIssuerName((char*)issuer, n); + BIO_free(out); + + // Get subject of issuer + unsigned char *subject; + out = BIO_new(BIO_s_mem()); + X509_NAME_print_ex(out, X509_get_subject_name(cert->m_data->cert), 0, XN_FLAG_RFC2253); + n = BIO_get_mem_data(out, &subject); + vmime::string subjOfIssuer((char*)subject, n); + BIO_free(out); + + return subjOfIssuer == thisIssuerName; +} + + +bool X509Certificate_OpenSSL::verify(ref caCert_) const +{ + ref caCert = + caCert_.dynamicCast (); + + + bool verified = false; + bool error = true; + + X509_STORE *store = X509_STORE_new(); + + if (store) + { + X509_STORE_CTX *verifyCtx = X509_STORE_CTX_new(); + + if (verifyCtx) + { + if (X509_STORE_add_cert(store, caCert->m_data->cert)) + { + X509_STORE_CTX_init(verifyCtx, store, m_data->cert, NULL); + + int ret = X509_verify_cert(verifyCtx); + + if (ret == 1) + { + verified = true; + error = false; + } + else if (ret == 0) + { + verified = false; + error = false; + } + + //X509_verify_cert_error_string(vrfy_ctx->error) + + X509_STORE_CTX_free(verifyCtx); + } + } + + X509_STORE_free(store); + } + + return verified && !error; +} + + +const datetime X509Certificate_OpenSSL::convertX509Date(void* time) const +{ + char* buffer; + BIO* out = BIO_new(BIO_s_mem()); + BIO_set_close(out, BIO_CLOSE); + + ASN1_TIME* asn1_time = reinterpret_cast(time); + ASN1_TIME_print(out, asn1_time); + + int sz = BIO_get_mem_data(out, &buffer); + char* dest = new char[sz + 1]; + dest[sz] = 0; + memcpy(dest, buffer, sz); + vmime::string t(dest); + + BIO_free(out); + delete dest; + + if (t.size() > 0) + { + char month[4] = {0}; + char zone[4] = {0}; + int day, hour, minute, second, year; + int nrconv = sscanf(t.c_str(), "%s %2d %02d:%02d:%02d %d%s", month, &day, &hour, &minute, &second,&year,zone); + + if (nrconv >= 6) + return datetime(year, sg_monthMap.getMonth(vmime::string(month)), day, hour, minute, second); + } + + // let datetime try and parse it + return datetime(t); +} + + +const datetime X509Certificate_OpenSSL::getActivationDate() const +{ + return convertX509Date(X509_get_notBefore(m_data->cert)); +} + + +const datetime X509Certificate_OpenSSL::getExpirationDate() const +{ + return convertX509Date(X509_get_notAfter(m_data->cert)); +} + + +const byteArray X509Certificate_OpenSSL::getFingerprint(const DigestAlgorithm algo) const +{ + BIO *out; + int j; + unsigned int n; + const EVP_MD *digest; + unsigned char * fingerprint, *result; + unsigned char md[EVP_MAX_MD_SIZE]; + + switch (algo) + { + case DIGEST_MD5: + + digest = EVP_md5(); + break; + + default: + case DIGEST_SHA1: + + digest = EVP_sha1(); + break; + } + + out = BIO_new(BIO_s_mem()); + BIO_set_close(out, BIO_CLOSE); + + if (X509_digest(m_data->cert, digest, md, &n)) + { + for (j=0; j<(int)n; j++) + { + BIO_printf (out, "%02X",md[j]); + if (j+1 != (int)n) BIO_printf(out, ":"); + } + } + + n = BIO_get_mem_data(out, &fingerprint); + result = new unsigned char[n]; + memcpy (result, fingerprint, n); + BIO_free(out); + + byteArray res; + res.insert(res.end(), &result[0], &result[0] + n); + + delete [] result; + + return res; +} + + +const byteArray X509Certificate_OpenSSL::getEncoded() const +{ + byteArray bytes; + utility::outputStreamByteArrayAdapter os(bytes); + + write(os, FORMAT_DER); + + return bytes; +} + + +const string X509Certificate_OpenSSL::getType() const +{ + return "X.509"; +} + + +int X509Certificate_OpenSSL::getVersion() const +{ + return (int)X509_get_version(m_data->cert); +} + + +bool X509Certificate_OpenSSL::equals(ref other) const +{ + ref otherX509 = + other.dynamicCast (); + + if (!otherX509) + return false; + + const byteArray fp1 = getFingerprint(DIGEST_MD5); + const byteArray fp2 = otherX509->getFingerprint(DIGEST_MD5); + + return fp1 == fp2; +} + + +} // cert +} // security +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + diff --git a/src/utility/sync/criticalSection.cpp b/src/utility/sync/criticalSection.cpp new file mode 100755 index 00000000..642467f1 --- /dev/null +++ b/src/utility/sync/criticalSection.cpp @@ -0,0 +1,44 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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/utility/sync/criticalSection.hpp" + + +namespace vmime { +namespace utility { +namespace sync { + + +criticalSection::criticalSection() +{ +} + + +criticalSection::~criticalSection() +{ +} + + +} // sync +} // utility +} // vmime diff --git a/vmime/net/tls/TLSSession.hpp b/vmime/net/tls/TLSSession.hpp index 75241c8c..8946e521 100644 --- a/vmime/net/tls/TLSSession.hpp +++ b/vmime/net/tls/TLSSession.hpp @@ -28,7 +28,7 @@ #include "vmime/config.hpp" -#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT #include "vmime/types.hpp" @@ -47,19 +47,15 @@ namespace tls { */ class TLSSession : public object { - friend class TLSSocket; - public: - ~TLSSession(); - /** Create and initialize a new TLS session. * * @param cv object responsible for verifying certificates * sent by the server * @return a new TLS session */ - TLSSession(ref cv); + static ref create(ref cv); /** Create a new socket that adds a TLS security layer around * an existing socket. You should create only one socket @@ -68,27 +64,20 @@ public: * @param sok socket to wrap * @return TLS socket wrapper */ - ref getSocket(ref sok); + virtual ref getSocket(ref sok) = 0; /** Get the object responsible for verifying certificates when * using secured connections (TLS/SSL). */ - ref getCertificateVerifier(); + virtual ref getCertificateVerifier() = 0; + +protected: + + TLSSession(); private: TLSSession(const TLSSession&); - - static void throwTLSException(const string& fname, const int code); - - -#ifdef LIBGNUTLS_VERSION - gnutls_session* m_gnutlsSession; -#else - void* m_gnutlsSession; -#endif // LIBGNUTLS_VERSION - - ref m_certVerifier; }; @@ -97,7 +86,6 @@ private: } // vmime -#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT #endif // VMIME_NET_TLS_TLSSESSION_HPP_INCLUDED - diff --git a/vmime/net/tls/TLSSocket.hpp b/vmime/net/tls/TLSSocket.hpp index e88d424c..bcc5d085 100644 --- a/vmime/net/tls/TLSSocket.hpp +++ b/vmime/net/tls/TLSSocket.hpp @@ -28,7 +28,7 @@ #include "vmime/config.hpp" -#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT #include "vmime/exception.hpp" @@ -51,9 +51,7 @@ class TLSSession; */ class TLSSocket : public socket { - friend class vmime::creator; - -protected: +public: /** Create a new socket object that adds a security layer * around an existing socket. @@ -61,12 +59,7 @@ protected: * @param session TLS session * @param sok socket to wrap */ - TLSSocket(ref session, ref sok); - -public: - - ~TLSSocket(); - + static ref wrap(ref session, ref sok); /** Starts a TLS handshake on this connection. * @@ -74,53 +67,14 @@ public: * during the negociation process, exceptions::operation_timed_out * if a time-out occurs */ - void handshake(ref toHandler = NULL); + virtual void handshake(ref toHandler = NULL) = 0; /** Return the peer's certificate (chain) as sent by the peer. * * @return server certificate chain, or NULL if the handshake * has not been performed yet */ - ref getPeerCertificates() const; - - - // Implementation of 'socket' - void connect(const string& address, const port_t port); - void disconnect(); - bool isConnected() const; - - void receive(string& buffer); - size_type receiveRaw(char* buffer, const size_type count); - - void send(const string& buffer); - void sendRaw(const char* buffer, const size_type count); - - size_type getBlockSize() const; - -private: - - void internalThrow(); - -#ifdef LIBGNUTLS_VERSION - static ssize_t gnutlsPushFunc(gnutls_transport_ptr trspt, const void* data, size_t len); - static ssize_t gnutlsPullFunc(gnutls_transport_ptr trspt, void* data, size_t len); -#else - static int gnutlsPushFunc(void* trspt, const void* data, size_t len); - static int gnutlsPullFunc(void* trspt, void* data, size_t len); -#endif // LIBGNUTLS_VERSION - - - ref m_session; - ref m_wrapped; - - bool m_connected; - - char m_buffer[65536]; - - bool m_handshaking; - ref m_toHandler; - - exception* m_ex; + virtual ref getPeerCertificates() const = 0; }; @@ -129,7 +83,6 @@ private: } // vmime -#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT #endif // VMIME_NET_TLS_TLSSOCKET_HPP_INCLUDED - diff --git a/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp b/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp new file mode 100644 index 00000000..d34054dd --- /dev/null +++ b/vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp @@ -0,0 +1,89 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession_GnuTLS : public TLSSession +{ + friend class TLSSocket_GnuTLS; + +public: + + TLSSession_GnuTLS(ref cv); + ~TLSSession_GnuTLS(); + + + ref getSocket(ref sok); + + ref getCertificateVerifier(); + +private: + + TLSSession_GnuTLS(const TLSSession_GnuTLS&); + + static void throwTLSException(const string& fname, const int code); + + +#ifdef LIBGNUTLS_VERSION + gnutls_session* m_gnutlsSession; +#else + void* m_gnutlsSession; +#endif // LIBGNUTLS_VERSION + + ref m_certVerifier; +}; + + +} // 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/net/tls/gnutls/TLSSocket_GnuTLS.hpp b/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp new file mode 100644 index 00000000..729923f3 --- /dev/null +++ b/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp @@ -0,0 +1,113 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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 +{ + friend class vmime::creator; + +public: + + TLSSocket_GnuTLS(ref session, ref sok); + ~TLSSocket_GnuTLS(); + + + void handshake(ref toHandler = NULL); + + ref getPeerCertificates() const; + + // Implementation of 'socket' + void connect(const string& address, const port_t port); + void disconnect(); + bool isConnected() const; + + void receive(string& buffer); + size_type receiveRaw(char* buffer, const size_type count); + + void send(const string& buffer); + void sendRaw(const char* buffer, const size_type count); + + size_type getBlockSize() const; + +private: + + void internalThrow(); + +#ifdef LIBGNUTLS_VERSION + static ssize_t gnutlsPushFunc(gnutls_transport_ptr trspt, const void* data, size_t len); + static ssize_t gnutlsPullFunc(gnutls_transport_ptr trspt, void* data, size_t len); +#else + static int gnutlsPushFunc(void* trspt, const void* data, size_t len); + static int gnutlsPullFunc(void* trspt, void* data, size_t len); +#endif // LIBGNUTLS_VERSION + + + ref m_session; + ref m_wrapped; + + bool m_connected; + + char m_buffer[65536]; + + bool m_handshaking; + ref m_toHandler; + + exception* m_ex; +}; + + +} // 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 + diff --git a/vmime/net/tls/openssl/OpenSSLInitializer.hpp b/vmime/net/tls/openssl/OpenSSLInitializer.hpp new file mode 100755 index 00000000..65c0aac6 --- /dev/null +++ b/vmime/net/tls/openssl/OpenSSLInitializer.hpp @@ -0,0 +1,102 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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 + + +#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 + */ + OpenSSLInitializer(); + + /** Automatically uninitialize OpenSSL + */ + ~OpenSSLInitializer(); + +protected: + + /** Initializes the OpenSSL lib + */ + static void initialize(); + + /** Shutdown the OpenSSL lib + */ + static void uninitialize(); + + + static ref getMutex(); + + enum + { + SEEDSIZE = 256 + }; + + + // OpenSSL multithreading support + static void lock(int mode, int n, const char* file, int line); + static unsigned long id(); + +private: + + static ref * sm_mutexes; + static int sm_initCount; +}; + + +} // 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/net/tls/openssl/TLSSession_OpenSSL.hpp b/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp new file mode 100755 index 00000000..1765597b --- /dev/null +++ b/vmime/net/tls/openssl/TLSSession_OpenSSL.hpp @@ -0,0 +1,106 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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 + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession_OpenSSL : public TLSSession +{ + friend class TLSSocket_OpenSSL; + +public: + + TLSSession_OpenSSL(const ref cv); + ~TLSSession_OpenSSL(); + + + ref getSocket(ref sok); + + ref getCertificateVerifier(); + + + /** Set the private key to use if server requires a client certificate. + * + * @param keyfile Path to the private key in PEM format + * @param passwd_callback If the private key is stored encrypted the + */ + 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; + + ref m_certVerifier; +}; + + +} // 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/net/tls/openssl/TLSSocket_OpenSSL.hpp b/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp new file mode 100755 index 00000000..401fad12 --- /dev/null +++ b/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp @@ -0,0 +1,116 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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 + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession; +class TLSSession_OpenSSL; + + +class TLSSocket_OpenSSL : public TLSSocket +{ + friend class vmime::creator; + +public: + + TLSSocket_OpenSSL(ref session, ref sok); + ~TLSSocket_OpenSSL(); + + + void handshake(ref toHandler = NULL); + + ref getPeerCertificates() const; + + // Implementation of 'socket' + void connect(const string& address, const port_t port); + void disconnect(); + bool isConnected() const; + + void receive(string& buffer); + size_type receiveRaw(char* buffer, const size_type count); + + void send(const string& buffer); + void sendRaw(const char* buffer, const size_type count); + + size_type getBlockSize() const; + +private: + + 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 handleError(int rc); + + ref m_session; + + ref m_wrapped; + + bool m_connected; + + char m_buffer[65536]; + + ref m_toHandler; + + SSL* m_ssl; +}; + + +} // 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 + diff --git a/vmime/platform.hpp b/vmime/platform.hpp index 2063268e..86ce8ee0 100644 --- a/vmime/platform.hpp +++ b/vmime/platform.hpp @@ -40,6 +40,8 @@ #include "vmime/utility/childProcess.hpp" #endif +#include "vmime/utility/sync/criticalSection.hpp" + namespace vmime { @@ -89,6 +91,13 @@ public: */ virtual unsigned int getProcessId() const = 0; + /** Return an unique identifier for the current thread. + * Used for multi-threading synchronization. + * + * @return current thread id + */ + virtual unsigned int getThreadId() const = 0; + /** Return the charset used on the system. * * @return locale charset @@ -126,6 +135,16 @@ public: virtual ref getChildProcessFactory() = 0; #endif + /** Fills a buffer with cryptographically random bytes. + * + * @param buffer buffer to fill in with random bytes + * @param count number of random bytes to write in buffer + */ + virtual void generateRandomBytes(unsigned char* buffer, const unsigned int count) = 0; + + /** Creates and initializes a critical section. + */ + virtual ref createCriticalSection() = 0; }; diff --git a/vmime/platforms/posix/posixCriticalSection.hpp b/vmime/platforms/posix/posixCriticalSection.hpp new file mode 100755 index 00000000..16dd9cd3 --- /dev/null +++ b/vmime/platforms/posix/posixCriticalSection.hpp @@ -0,0 +1,69 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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_PLATFORMS_POSIX_CRITICALSECTION_HPP_INCLUDED +#define VMIME_PLATFORMS_POSIX_CRITICALSECTION_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_PLATFORM_IS_POSIX + + +#include "vmime/utility/sync/criticalSection.hpp" + + +#include +#include + + +namespace vmime { +namespace platforms { +namespace posix { + + +class posixCriticalSection : public utility::sync::criticalSection +{ +public: + + posixCriticalSection(); + ~posixCriticalSection(); + + void lock(); + void unlock(); + +private: + + pthread_mutex_t m_cs; +}; + + +} // posix +} // platforms +} // vmime + + +#endif // VMIME_PLATFORM_IS_POSIX + +#endif // VMIME_PLATFORMS_POSIX_CRITICALSECTION_HPP_INCLUDED diff --git a/vmime/platforms/posix/posixHandler.hpp b/vmime/platforms/posix/posixHandler.hpp index 80cfb650..b6ef3df6 100644 --- a/vmime/platforms/posix/posixHandler.hpp +++ b/vmime/platforms/posix/posixHandler.hpp @@ -64,6 +64,7 @@ public: const vmime::string getHostName() const; unsigned int getProcessId() const; + unsigned int getThreadId() const; #if VMIME_HAVE_MESSAGING_FEATURES ref getSocketFactory(); @@ -77,6 +78,10 @@ public: void wait() const; + void generateRandomBytes(unsigned char* buffer, const unsigned int count); + + ref createCriticalSection(); + private: #if VMIME_HAVE_MESSAGING_FEATURES diff --git a/vmime/platforms/windows/windowsCriticalSection.hpp b/vmime/platforms/windows/windowsCriticalSection.hpp new file mode 100755 index 00000000..0da3dafb --- /dev/null +++ b/vmime/platforms/windows/windowsCriticalSection.hpp @@ -0,0 +1,68 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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_PLATFORMS_WINDOWS_CRITICALSECTION_HPP_INCLUDED +#define VMIME_PLATFORMS_WINDOWS_CRITICALSECTION_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_PLATFORM_IS_WINDOWS + + +#include "vmime/utility/sync/criticalSection.hpp" + + +#include + + +namespace vmime { +namespace platforms { +namespace windows { + + +class windowsCriticalSection : public utility::sync::criticalSection +{ +public: + + windowsCriticalSection(); + ~windowsCriticalSection(); + + void lock(); + void unlock(); + +private: + + CRITICAL_SECTION m_cs; +}; + + +} // windows +} // platforms +} // vmime + + +#endif // VMIME_PLATFORM_IS_WINDOWS + +#endif // VMIME_PLATFORMS_WINDOWS_CRITICALSECTION_HPP_INCLUDED diff --git a/vmime/platforms/windows/windowsHandler.hpp b/vmime/platforms/windows/windowsHandler.hpp index 4f0150f8..ceccae2b 100644 --- a/vmime/platforms/windows/windowsHandler.hpp +++ b/vmime/platforms/windows/windowsHandler.hpp @@ -63,6 +63,7 @@ public: const vmime::string getHostName() const; unsigned int getProcessId() const; + unsigned int getThreadId() const; #if VMIME_HAVE_MESSAGING_FEATURES ref getSocketFactory(); @@ -76,6 +77,10 @@ public: void wait() const; + void generateRandomBytes(unsigned char* buffer, const unsigned int count); + + ref createCriticalSection(); + private: #if VMIME_HAVE_MESSAGING_FEATURES diff --git a/vmime/security/cert/X509Certificate.hpp b/vmime/security/cert/X509Certificate.hpp index 48fc882c..b7f0b946 100644 --- a/vmime/security/cert/X509Certificate.hpp +++ b/vmime/security/cert/X509Certificate.hpp @@ -25,6 +25,12 @@ #define VMIME_SECURITY_CERT_X509CERTIFICATE_HPP_INCLUDED +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + + #include "vmime/security/cert/certificate.hpp" #include "vmime/utility/stream.hpp" @@ -42,13 +48,6 @@ namespace cert { */ class X509Certificate : public certificate { - friend class vmime::creator; - -protected: - - X509Certificate(); - X509Certificate(const X509Certificate&); - public: ~X509Certificate(); @@ -90,7 +89,7 @@ public: * @param os output stream into which write data * @param format output format */ - void write(utility::outputStream& os, const Format format) const; + virtual void write(utility::outputStream& os, const Format format) const = 0; /** Returns the X.509 certificate's serial number. This is obtained * by the X.509 Certificate 'serialNumber' field. Serial is not @@ -99,7 +98,7 @@ public: * * @return serial number of this certificate */ - const byteArray getSerialNumber() const; + virtual const byteArray getSerialNumber() const = 0; /** Checks if this certificate has the given issuer. * @@ -107,45 +106,34 @@ public: * @return true if this certificate was issued by the given issuer, * false otherwise */ - bool checkIssuer(ref issuer) const; + virtual bool checkIssuer(ref issuer) const = 0; /** Verifies this certificate against a given trusted one. * * @param caCert a certificate that is considered to be trusted one * @return true if the verification succeeded, false otherwise */ - bool verify(ref caCert) const; + virtual bool verify(ref caCert) const = 0; /** Gets the expiration date of this certificate. This is the date * at which this certificate will not be valid anymore. * * @return expiration date of this certificate */ - const datetime getExpirationDate() const; + virtual const datetime getExpirationDate() const = 0; /** Gets the activation date of this certificate. This is the date * at which this certificate will be valid. * * @return activation date of this certificate */ - const datetime getActivationDate() const; + virtual const datetime getActivationDate() const = 0; /** Returns the fingerprint of this certificate. * * @return the fingerprint of this certificate */ - const byteArray getFingerprint(const DigestAlgorithm algo) const; - - - // Implementation of 'certificate' - const byteArray getEncoded() const; - const string getType() const; - int getVersion() const; - bool equals(ref other) const; - -private: - - struct X509CertificateInternalData* m_data; + virtual const byteArray getFingerprint(const DigestAlgorithm algo) const = 0; }; @@ -154,5 +142,7 @@ private: } // vmime +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT + #endif // VMIME_SECURITY_CERT_X509CERTIFICATE_HPP_INCLUDED diff --git a/vmime/security/cert/gnutls/X509Certificate_GnuTLS.hpp b/vmime/security/cert/gnutls/X509Certificate_GnuTLS.hpp new file mode 100644 index 00000000..c720c1fb --- /dev/null +++ b/vmime/security/cert/gnutls/X509Certificate_GnuTLS.hpp @@ -0,0 +1,91 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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_SECURITY_CERT_X509CERTIFICATE_GNUTLS_HPP_INCLUDED +#define VMIME_SECURITY_CERT_X509CERTIFICATE_GNUTLS_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + + +#include "vmime/security/cert/X509Certificate.hpp" + + +namespace vmime { +namespace security { +namespace cert { + + +class X509Certificate_GnuTLS : public X509Certificate +{ + friend class vmime::creator; + friend class X509Certificate; + +protected: + + X509Certificate_GnuTLS(); + X509Certificate_GnuTLS(const X509Certificate&); + +public: + + ~X509Certificate_GnuTLS(); + + + void write(utility::outputStream& os, const Format format) const; + + const byteArray getSerialNumber() const; + + bool checkIssuer(ref issuer) const; + + bool verify(ref caCert) const; + + const datetime getExpirationDate() const; + const datetime getActivationDate() const; + + const byteArray getFingerprint(const DigestAlgorithm algo) const; + + + // Implementation of 'certificate' + const byteArray getEncoded() const; + const string getType() const; + int getVersion() const; + bool equals(ref other) const; + +private: + + struct GnuTLSX509CertificateInternalData* m_data; +}; + + +} // cert +} // security +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_GNUTLS + +#endif // VMIME_SECURITY_CERT_X509CERTIFICATE_GNUTLS_HPP_INCLUDED + diff --git a/vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp b/vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp new file mode 100644 index 00000000..d9083b06 --- /dev/null +++ b/vmime/security/cert/openssl/X509Certificate_OpenSSL.hpp @@ -0,0 +1,104 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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_SECURITY_CERT_X509CERTIFICATE_OPENSSL_HPP_INCLUDED +#define VMIME_SECURITY_CERT_X509CERTIFICATE_OPENSSL_HPP_INCLUDED + + +#include "vmime/config.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + + +#include "vmime/security/cert/X509Certificate.hpp" + +#include + + +namespace vmime { +namespace security { +namespace cert { + + +class X509Certificate_OpenSSL : public X509Certificate +{ + friend class vmime::creator; + friend class X509Certificate; + +protected: + + X509Certificate_OpenSSL(); + X509Certificate_OpenSSL(X509* cert); + X509Certificate_OpenSSL(const X509Certificate_OpenSSL&); + +public: + + ~X509Certificate_OpenSSL(); + + + void write(utility::outputStream& os, const Format format) const; + + const byteArray getSerialNumber() const; + + bool checkIssuer(ref issuer) const; + + bool verify(ref caCert) const; + + const datetime getExpirationDate() const; + const datetime getActivationDate() const; + + const byteArray getFingerprint(const DigestAlgorithm algo) const; + + + static ref importInternal(X509* cert); + + + // Implementation of 'certificate' + const byteArray getEncoded() const; + const string getType() const; + int getVersion() const; + bool equals(ref other) const; + +private: + + /** Internal utility function to convert ASN1_TIME + * structs to vmime::datetime + * + * @param pointer to ASN1_TIME struct to convert + */ + const datetime convertX509Date(void* time) const; + + struct OpenSSLX509CertificateInternalData* m_data; +}; + + +} // cert +} // security +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES && VMIME_HAVE_TLS_SUPPORT && VMIME_TLS_SUPPORT_LIB_IS_OPENSSL + +#endif // VMIME_SECURITY_CERT_X509CERTIFICATE_OPENSSL_HPP_INCLUDED + diff --git a/vmime/utility/sync/autoLock.hpp b/vmime/utility/sync/autoLock.hpp new file mode 100755 index 00000000..1675b78f --- /dev/null +++ b/vmime/utility/sync/autoLock.hpp @@ -0,0 +1,66 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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_UTILITY_SYNC_AUTOLOCK_HPP_INCLUDED +#define VMIME_UTILITY_SYNC_AUTOLOCK_HPP_INCLUDED + + +#include "vmime/base.hpp" + + +namespace vmime { +namespace utility { +namespace sync { + + +/** Critical section wrapper class + */ + +template +class autoLock : public object +{ +public: + + autoLock(ref mutex) + : m_mutex(mutex) + { + m_mutex->lock(); + } + + ~autoLock() + { + m_mutex->unlock(); + } + +private: + + ref m_mutex; +}; + + +} // sync +} // utility +} // vmime + + +#endif // VMIME_UTILITY_SYNC_AUTOLOCK_HPP_INCLUDED diff --git a/vmime/utility/sync/criticalSection.hpp b/vmime/utility/sync/criticalSection.hpp new file mode 100755 index 00000000..6a2dc52b --- /dev/null +++ b/vmime/utility/sync/criticalSection.hpp @@ -0,0 +1,65 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2009 Vincent Richard +// +// 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_UTILITY_SYNC_CRITICALSECTION_HPP_INCLUDED +#define VMIME_UTILITY_SYNC_CRITICALSECTION_HPP_INCLUDED + + +#include "vmime/base.hpp" + + +namespace vmime { +namespace utility { +namespace sync { + + +/** Critical section class. + */ + +class criticalSection : public object +{ +public: + + virtual ~criticalSection(); + + /** Enters the critical section. + */ + virtual void lock() = 0; + + /** Leaves the critical section. + */ + virtual void unlock() = 0; + +protected: + + criticalSection(); + criticalSection(criticalSection&); +}; + + +} // sync +} // utility +} // vmime + + +#endif // VMIME_UTILITY_SYNC_CRITICALSECTION_HPP_INCLUDED