From 7d2d25da3eb891a70ec5dfd3b077b0e130571472 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Mon, 3 Oct 2005 21:29:04 +0000 Subject: [PATCH] Added TLS/SSL support. --- ChangeLog | 4 + SConstruct | 171 +++++--- examples/README | 7 +- examples/example6.cpp | 118 +++++- m4/libgnutls.m4 | 160 +++++++ src/exception.cpp | 54 +++ src/net/authHelper.cpp | 111 ----- src/net/builtinServices.inl | 30 +- src/net/imap/IMAPConnection.cpp | 112 ++++- src/net/imap/IMAPSStore.cpp | 65 +++ src/net/imap/IMAPServiceInfos.cpp | 133 ++++++ src/net/imap/IMAPStore.cpp | 73 +--- src/net/maildir/maildirServiceInfos.cpp | 64 +++ src/net/maildir/maildirStore.cpp | 42 +- src/net/pop3/POP3SStore.cpp | 66 +++ src/net/pop3/POP3ServiceInfos.cpp | 139 +++++++ src/net/pop3/POP3Store.cpp | 191 +++++---- src/net/sendmail/sendmailServiceInfos.cpp | 65 +++ src/net/sendmail/sendmailTransport.cpp | 41 +- src/net/service.cpp | 25 ++ src/net/serviceInfos.cpp | 10 + src/net/smtp/SMTPSTransport.cpp | 66 +++ src/net/smtp/SMTPServiceInfos.cpp | 136 ++++++ src/net/smtp/SMTPTransport.cpp | 169 +++++--- src/net/tls/TLSSession.cpp | 342 +++++++++++++++ src/net/tls/TLSSocket.cpp | 391 ++++++++++++++++++ src/net/tls/X509Certificate.cpp | 273 ++++++++++++ .../net/tls/certificateChain.cpp | 27 +- src/net/tls/defaultCertificateVerifier.cpp | 164 ++++++++ src/utility/stream.cpp | 16 + vmime.doxygen | 2 +- vmime/exception.hpp | 67 ++- vmime/net/imap/IMAPConnection.hpp | 4 + vmime/net/imap/IMAPParser.hpp | 5 + vmime/net/imap/IMAPSStore.hpp | 63 +++ vmime/net/imap/IMAPServiceInfos.hpp | 87 ++++ vmime/net/imap/IMAPStore.hpp | 41 +- vmime/net/maildir/maildirServiceInfos.hpp | 64 +++ vmime/net/maildir/maildirStore.hpp | 19 +- vmime/net/pop3/POP3SStore.hpp | 63 +++ vmime/net/pop3/POP3ServiceInfos.hpp | 89 ++++ vmime/net/pop3/POP3Store.hpp | 43 +- vmime/net/sendmail/sendmailServiceInfos.hpp | 64 +++ vmime/net/sendmail/sendmailTransport.hpp | 19 +- vmime/net/service.hpp | 30 +- vmime/net/serviceFactory.hpp | 18 +- vmime/net/serviceInfos.hpp | 17 + vmime/net/smtp/SMTPSTransport.hpp | 63 +++ vmime/net/smtp/SMTPServiceInfos.hpp | 88 ++++ vmime/net/smtp/SMTPTransport.hpp | 41 +- vmime/net/tls/TLSSession.hpp | 95 +++++ vmime/net/tls/TLSSocket.hpp | 125 ++++++ vmime/net/tls/X509Certificate.hpp | 158 +++++++ vmime/net/tls/certificate.hpp | 77 ++++ vmime/net/tls/certificateChain.hpp | 79 ++++ vmime/net/tls/certificateVerifier.hpp | 60 +++ vmime/net/tls/defaultCertificateVerifier.hpp | 88 ++++ vmime/utility/stream.hpp | 17 + vmime/vmime.hpp | 13 + 59 files changed, 4275 insertions(+), 589 deletions(-) create mode 100644 m4/libgnutls.m4 delete mode 100644 src/net/authHelper.cpp create mode 100644 src/net/imap/IMAPSStore.cpp create mode 100644 src/net/imap/IMAPServiceInfos.cpp create mode 100644 src/net/maildir/maildirServiceInfos.cpp create mode 100644 src/net/pop3/POP3SStore.cpp create mode 100644 src/net/pop3/POP3ServiceInfos.cpp create mode 100644 src/net/sendmail/sendmailServiceInfos.cpp create mode 100644 src/net/smtp/SMTPSTransport.cpp create mode 100644 src/net/smtp/SMTPServiceInfos.cpp create mode 100644 src/net/tls/TLSSession.cpp create mode 100644 src/net/tls/TLSSocket.cpp create mode 100644 src/net/tls/X509Certificate.cpp rename vmime/net/authHelper.hpp => src/net/tls/certificateChain.cpp (74%) create mode 100644 src/net/tls/defaultCertificateVerifier.cpp create mode 100644 vmime/net/imap/IMAPSStore.hpp create mode 100644 vmime/net/imap/IMAPServiceInfos.hpp create mode 100644 vmime/net/maildir/maildirServiceInfos.hpp create mode 100644 vmime/net/pop3/POP3SStore.hpp create mode 100644 vmime/net/pop3/POP3ServiceInfos.hpp create mode 100644 vmime/net/sendmail/sendmailServiceInfos.hpp create mode 100644 vmime/net/smtp/SMTPSTransport.hpp create mode 100644 vmime/net/smtp/SMTPServiceInfos.hpp create mode 100644 vmime/net/tls/TLSSession.hpp create mode 100644 vmime/net/tls/TLSSocket.hpp create mode 100644 vmime/net/tls/X509Certificate.hpp create mode 100644 vmime/net/tls/certificate.hpp create mode 100644 vmime/net/tls/certificateChain.hpp create mode 100644 vmime/net/tls/certificateVerifier.hpp create mode 100644 vmime/net/tls/defaultCertificateVerifier.hpp diff --git a/ChangeLog b/ChangeLog index bcdea3a3..a4775a5c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,10 @@ VERSION 0.7.2cvs ================ +2005-10-03 Vincent Richard + + * Added TLS/SSL support, using GNU TLS library. + 2005-09-17 Vincent Richard * Added SASL support, based on GNU SASL library. Slightly modified diff --git a/SConstruct b/SConstruct index 0f24b6e8..130a3345 100644 --- a/SConstruct +++ b/SConstruct @@ -26,33 +26,26 @@ import string # Version # ############# -# How to increment version number when building a public release? -# =============================================================== -# -# Changing package version number: -# -# * Increment major number if major changes have been made to the library, -# that is, program depending on this library will need to be changed to -# work with the new major release. -# -# * Increment minor number when the changes are upward-compatible: some -# interfaces have been added, but compatibility is maintained for -# existing interfaces. -# -# * Increment micro number when the changes do not add any new interface. -# The changes only apply to the implementation (bug or security fix, -# performance improvement, etc.). -# - # Package version number packageVersionMajor = 0 packageVersionMinor = 7 packageVersionMicro = 2 -# Shared library version number (computed from package version number) -packageAPICurrent = packageVersionMajor + packageVersionMinor -packageAPIRevision = packageVersionMicro -packageAPIAge = packageVersionMinor +# API version number (libtool) +# +# Increment this number only immediately before a public release. +# This is independant from package version number. +# +# See: http://www.gnu.org/software/libtool/manual.html#Libtool-versioning +# +# . Implementation changed (eg. bug/security fix): REVISION++ +# . Interfaces added/removed/changed: CURRENT++, REVISION=0 +# . Interfaces added (upward-compatible changes): AGE++ +# . Interfaces removed: AGE=0 +# +packageAPICurrent = 0 +packageAPIRevision = 0 +packageAPIAge = 0 # Package information packageName = 'libvmime' @@ -204,7 +197,6 @@ libvmime_examples_sources = [ ] libvmime_messaging_sources = [ - 'net/authHelper.cpp', 'net/authHelper.hpp', 'net/builtinServices.inl', 'net/events.cpp', 'net/events.hpp', 'net/folder.cpp', 'net/folder.hpp', @@ -219,46 +211,64 @@ libvmime_messaging_sources = [ 'net/transport.cpp', 'net/transport.hpp' ] +libvmime_net_tls_sources = [ + 'net/tls/TLSSession.cpp', 'net/tls/TLSSession.hpp', + 'net/tls/TLSSocket.cpp', 'net/tls/TLSSocket.hpp', + 'net/tls/certificateChain.cpp', 'net/tls/certificateChain.hpp', + 'net/tls/certificateVerifier.hpp', + 'net/tls/defaultCertificateVerifier.cpp', 'net/tls/defaultCertificateVerifier.hpp', + 'net/tls/certificate.hpp', + 'net/tls/X509Certificate.cpp', 'net/tls/X509Certificate.hpp' +] + libvmime_messaging_proto_sources = [ [ 'pop3', [ - 'net/pop3/POP3Store.cpp', 'net/pop3/POP3Store.hpp', - 'net/pop3/POP3Folder.cpp', 'net/pop3/POP3Folder.hpp', - 'net/pop3/POP3Message.cpp', 'net/pop3/POP3Message.hpp' + 'net/pop3/POP3ServiceInfos.cpp', 'net/pop3/POP3ServiceInfos.hpp', + 'net/pop3/POP3Store.cpp', 'net/pop3/POP3Store.hpp', + 'net/pop3/POP3SStore.cpp', 'net/pop3/POP3SStore.hpp', + 'net/pop3/POP3Folder.cpp', 'net/pop3/POP3Folder.hpp', + 'net/pop3/POP3Message.cpp', 'net/pop3/POP3Message.hpp' ] ], [ 'smtp', [ - 'net/smtp/SMTPTransport.cpp', 'net/smtp/SMTPTransport.hpp' + 'net/smtp/SMTPServiceInfos.cpp', 'net/smtp/SMTPServiceInfos.hpp', + 'net/smtp/SMTPTransport.cpp', 'net/smtp/SMTPTransport.hpp', + 'net/smtp/SMTPSTransport.cpp', 'net/smtp/SMTPSTransport.hpp' ] ], [ 'imap', [ - 'net/imap/IMAPConnection.cpp', 'net/imap/IMAPConnection.hpp', - 'net/imap/IMAPStore.cpp', 'net/imap/IMAPStore.hpp', - 'net/imap/IMAPFolder.cpp', 'net/imap/IMAPFolder.hpp', - 'net/imap/IMAPMessage.cpp', 'net/imap/IMAPMessage.hpp', - 'net/imap/IMAPTag.cpp', 'net/imap/IMAPTag.hpp', - 'net/imap/IMAPUtils.cpp', 'net/imap/IMAPUtils.hpp', - 'net/imap/IMAPParser.hpp' + 'net/imap/IMAPServiceInfos.cpp', 'net/imap/IMAPServiceInfos.hpp', + 'net/imap/IMAPConnection.cpp', 'net/imap/IMAPConnection.hpp', + 'net/imap/IMAPStore.cpp', 'net/imap/IMAPStore.hpp', + 'net/imap/IMAPSStore.cpp', 'net/imap/IMAPSStore.hpp', + 'net/imap/IMAPFolder.cpp', 'net/imap/IMAPFolder.hpp', + 'net/imap/IMAPMessage.cpp', 'net/imap/IMAPMessage.hpp', + 'net/imap/IMAPTag.cpp', 'net/imap/IMAPTag.hpp', + 'net/imap/IMAPUtils.cpp', 'net/imap/IMAPUtils.hpp', + 'net/imap/IMAPParser.hpp', ] ], [ 'maildir', [ - 'net/maildir/maildirStore.cpp', 'net/maildir/maildirStore.hpp', - 'net/maildir/maildirFolder.cpp', 'net/maildir/maildirFolder.hpp', - 'net/maildir/maildirMessage.cpp', 'net/maildir/maildirMessage.hpp', - 'net/maildir/maildirUtils.cpp', 'net/maildir/maildirUtils.hpp' + 'net/maildir/maildirServiceInfos.cpp', 'net/maildir/maildirServiceInfos.hpp', + 'net/maildir/maildirStore.cpp', 'net/maildir/maildirStore.hpp', + 'net/maildir/maildirFolder.cpp', 'net/maildir/maildirFolder.hpp', + 'net/maildir/maildirMessage.cpp', 'net/maildir/maildirMessage.hpp', + 'net/maildir/maildirUtils.cpp', 'net/maildir/maildirUtils.hpp' ] ], [ 'sendmail', [ - 'net/sendmail/sendmailTransport.cpp', 'net/sendmail/sendmailTransport.hpp' + 'net/sendmail/sendmailServiceInfos.cpp', 'net/sendmail/sendmailServiceInfos.hpp', + 'net/sendmail/sendmailTransport.cpp', 'net/sendmail/sendmailTransport.hpp' ] ] ] @@ -348,6 +358,7 @@ libvmime_autotools = [ 'm4/lib-ld.m4', 'm4/lib-link.m4', 'm4/lib-prefix.m4', + 'm4/libgnutls.m4', 'autotools/install-sh', # 'autotools/mkinstalldirs', 'autotools/missing', @@ -367,7 +378,7 @@ libvmime_autotools = [ 'vmime/Makefile.in' ] -libvmime_all_sources = [] + libvmime_sources + libvmime_messaging_sources + libvmime_security_sasl_sources +libvmime_all_sources = [] + libvmime_sources + libvmime_messaging_sources + libvmime_security_sasl_sources + libvmime_net_tls_sources for i in range(len(libvmime_all_sources)): f = libvmime_all_sources[i] @@ -481,6 +492,14 @@ opts.AddOptions( map = { }, ignorecase = 1 ), + EnumOption( + 'with_tls', + 'Enable TLS support (requires GNU TLS library)', + 'yes', + allowed_values = ('yes', 'no'), + map = { }, + ignorecase = 1 + ), ( 'sendmail_path', 'Specifies the path to sendmail.', @@ -559,6 +578,8 @@ env.Append(CXXFLAGS = ['-pedantic']) env.Append(CXXFLAGS = ['-Wpointer-arith']) env.Append(CXXFLAGS = ['-Wold-style-cast']) env.Append(CXXFLAGS = ['-Wconversion']) +env.Append(CXXFLAGS = ['-Wcast-align']) +#env.Append(CXXFLAGS = ['-Wshadow']) env.Append(TARFLAGS = ['-c']) env.Append(TARFLAGS = ['--bzip2']) @@ -572,7 +593,11 @@ else: #env.Append(LIBS = ['additional-lib-here']) -env.ParseConfig('pkg-config --cflags --libs libgsasl') +if env['with_sasl'] == 'yes': + env.ParseConfig('pkg-config --cflags --libs libgsasl') + +if env['with_tls'] == 'yes': + env.ParseConfig('pkg-config --cflags --libs libgnutls') # Generate help text for command line options Help(opts.GenerateHelpText(env)) @@ -663,6 +688,7 @@ if env['with_messaging'] == 'yes': print "File-system support : " + env['with_filesystem'] print "Platform handlers : " + env['with_platforms'] print "SASL support : " + env['with_sasl'] +print "TLS/SSL support : " + env['with_tls'] if IsProtocolSupported(messaging_protocols, 'sendmail'): print "Sendmail path : " + env['sendmail_path'] @@ -754,6 +780,12 @@ if env['with_sasl'] == 'yes': else: config_hpp.write('#define VMIME_HAVE_SASL_SUPPORT 0\n') +config_hpp.write('// -- TLS/SSL support\n') +if env['with_tls'] == 'yes': + config_hpp.write('#define VMIME_HAVE_TLS_SUPPORT 1\n') +else: + config_hpp.write('#define VMIME_HAVE_TLS_SUPPORT 0\n') + config_hpp.write('// -- Messaging support\n') if env['with_messaging'] == 'yes': config_hpp.write('#define VMIME_HAVE_MESSAGING_FEATURES 1\n') @@ -832,6 +864,11 @@ if env['with_sasl'] == 'yes': for file in libvmime_security_sasl_sources: libvmime_sel_sources.append(file) +# -- TLS support +if env['with_tls'] == 'yes': + for file in libvmime_net_tls_sources: + libvmime_sel_sources.append(file) + # -- platform handlers for platform in platforms: files = libvmime_platforms_sources[platform] @@ -1010,9 +1047,9 @@ def generateAutotools(target, source, env): vmime_pc_in.write("Description: " + packageDescription + "\n") vmime_pc_in.write("Version: @VERSION@\n") vmime_pc_in.write("Requires: @GSASL_REQUIRED@\n") - vmime_pc_in.write("Libs: -L${libdir} -l@GENERIC_VERSIONED_LIBRARY_NAME@ @GSASL_LIBS@\n") + vmime_pc_in.write("Libs: -L${libdir} -l@GENERIC_VERSIONED_LIBRARY_NAME@ @GSASL_LIBS@ @LIBGNUTLS_LIBS@\n") #vmime_pc_in.write("Cflags: -I${includedir}/@GENERIC_VERSIONED_LIBRARY_NAME@\n") - vmime_pc_in.write("Cflags: -I${includedir}/\n") + vmime_pc_in.write("Cflags: -I${includedir}/ @LIBGNUTLS_CFLAGS@\n") vmime_pc_in.close() # Generate 'Makefile.am' @@ -1123,6 +1160,15 @@ INCLUDES = -I$(top_srcdir) -I$(srcdir) @PKGCONFIG_CFLAGS@ @EXTRA_CFLAGS@ Makefile_am.write(packageVersionedName + "_la_SOURCES += " + buildMakefileFileList(x, 1) + "\n") Makefile_am.write("endif\n") + # -- TLS support + x = selectFilesFromSuffixNot(libvmime_net_tls_sources, '.hpp') + sourceFiles += x + + Makefile_am.write("\n") + Makefile_am.write("if VMIME_HAVE_TLS_SUPPORT\n") + Makefile_am.write(packageVersionedName + "_la_SOURCES += " + buildMakefileFileList(x, 1) + "\n") + Makefile_am.write("endif\n") + # -- platform handlers for platform in libvmime_platforms_sources: Makefile_am.write("\n") @@ -1256,6 +1302,9 @@ AC_CHECK_HEADER(gsasl.h, have_gsasl=no), have_gsasl=no) +# -- GNU TLS Library (http://www.gnu.org/software/gnutls/) +AM_PATH_LIBGNUTLS(1.2.0, have_gnutls=yes, have_gnutls=no) + # -- global constructors (stolen from 'configure.in' in libsigc++) AC_MSG_CHECKING([if linker supports global constructors]) cat > mylib.$ac_ext < mech) { - std::cout << "Trying " << mech->getName() << std::endl; + std::cout << "Trying '" << mech->getName() << "' authentication mechanism" << std::endl; defaultSASLAuthenticator::setSASLMechanism(mech); } @@ -98,6 +98,94 @@ private: }; +#if VMIME_HAVE_TLS_SUPPORT + +// Certificate verifier (TLS/SSL) +class interactiveCertificateVerifier : public vmime::net::tls::defaultCertificateVerifier +{ +public: + + void verify(vmime::ref chain) + { + try + { + setX509TrustedCerts(m_trustedCerts); + + defaultCertificateVerifier::verify(chain); + } + catch (vmime::exceptions::certificate_verification_exception&) + { + // Obtain subject's certificate + vmime::ref cert = chain->getAt(0); + + std::cout << std::endl; + std::cout << "Server sent a '" << cert->getType() << "'" << " certificate." << std::endl; + std::cout << "Do you want to accept this certificate? (Y/n) "; + std::cout.flush(); + + std::string answer; + std::getline(std::cin, answer); + + if (answer.length() != 0 && + (answer[0] == 'Y' || answer[0] == 'y')) + { + // Accept it, and remember user's choice for later + if (cert->getType() == "X.509") + { + m_trustedCerts.push_back(cert.dynamicCast + ()); + } + + return; + } + + throw vmime::exceptions::certificate_verification_exception + ("User did not accept the certificate."); + } + } + +private: + + static std::vector > m_trustedCerts; +}; + + +std::vector > + interactiveCertificateVerifier::m_trustedCerts; + +#endif // VMIME_HAVE_TLS_SUPPORT + + +/** Returns the messaging protocols supported by VMime. + * + * @param type service type (vmime::net::service::TYPE_STORE or + * vmime::net::service::TYPE_TRANSPORT) + */ +static const std::string findAvailableProtocols(const vmime::net::service::Type type) +{ + vmime::net::serviceFactory* sf = vmime::net::serviceFactory::getInstance(); + + std::ostringstream res; + int count = 0; + + for (int i = 0 ; i < sf->getServiceCount() ; ++i) + { + const vmime::net::serviceFactory::registeredService& serv = *sf->getServiceAt(i); + + if (serv.getType() == type) + { + if (count != 0) + res << ", "; + + res << serv.getName(); + ++count; + } + } + + return res.str(); +} + + // Exception helper static std::ostream& operator<<(std::ostream& os, const vmime::exception& e) { @@ -244,6 +332,7 @@ static void sendMessage() { // Request user to enter an URL std::cout << "Enter an URL to connect to transport service." << std::endl; + std::cout << "Available protocols: " << findAvailableProtocols(vmime::net::service::TYPE_TRANSPORT) << std::endl; std::cout << "(eg. smtp://myserver.com, sendmail://localhost)" << std::endl; std::cout << "> "; std::cout.flush(); @@ -256,9 +345,21 @@ static void sendMessage() vmime::ref tr = g_session->getTransport(url, vmime::create ()); +#if VMIME_HAVE_TLS_SUPPORT + + // Enable TLS support if available + tr->setProperty("connection.tls", true); + + // Set the object responsible for verifying certificates, in the + // case a secured connection is used (TLS/SSL) + tr->setCertificateVerifier + (vmime::create ()); + +#endif // VMIME_HAVE_TLS_SUPPORT + // You can also set some properties (see example7 to know the properties // available for each service). For example, for SMTP: - tr->setProperty("options.need-authentication", true); +// tr->setProperty("options.need-authentication", true); // Information about the mail std::cout << "Enter email of the expeditor (eg. me@somewhere.com): "; @@ -338,6 +439,7 @@ static void connectStore() { // Request user to enter an URL std::cout << "Enter an URL to connect to store service." << std::endl; + std::cout << "Available protocols: " << findAvailableProtocols(vmime::net::service::TYPE_STORE) << std::endl; std::cout << "(eg. pop3://user:pass@myserver.com, imap://myserver.com:123)" << std::endl; std::cout << "> "; std::cout.flush(); @@ -357,6 +459,18 @@ static void connectStore() else st = g_session->getStore(url); +#if VMIME_HAVE_TLS_SUPPORT + + // Enable TLS support if available + st->setProperty("connection.tls", true); + + // Set the object responsible for verifying certificates, in the + // case a secured connection is used (TLS/SSL) + st->setCertificateVerifier + (vmime::create ()); + +#endif // VMIME_HAVE_TLS_SUPPORT + // Connect to the mail store st->connect(); diff --git a/m4/libgnutls.m4 b/m4/libgnutls.m4 new file mode 100644 index 00000000..99f0aa27 --- /dev/null +++ b/m4/libgnutls.m4 @@ -0,0 +1,160 @@ +dnl Autoconf macros for libgnutls +dnl $id$ + +# Modified for LIBGNUTLS -- nmav +# Configure paths for LIBGCRYPT +# Shamelessly stolen from the one of XDELTA by Owen Taylor +# Werner Koch 99-12-09 + +dnl AM_PATH_LIBGNUTLS([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) +dnl Test for libgnutls, and define LIBGNUTLS_CFLAGS and LIBGNUTLS_LIBS +dnl +AC_DEFUN([AM_PATH_LIBGNUTLS], +[dnl +dnl Get the cflags and libraries from the libgnutls-config script +dnl +AC_ARG_WITH(libgnutls-prefix, + [ --with-libgnutls-prefix=PFX Prefix where libgnutls is installed (optional)], + libgnutls_config_prefix="$withval", libgnutls_config_prefix="") + + if test x$libgnutls_config_prefix != x ; then + if test x${LIBGNUTLS_CONFIG+set} != xset ; then + LIBGNUTLS_CONFIG=$libgnutls_config_prefix/bin/libgnutls-config + fi + fi + + AC_PATH_PROG(LIBGNUTLS_CONFIG, libgnutls-config, no) + min_libgnutls_version=ifelse([$1], ,0.1.0,$1) + AC_MSG_CHECKING(for libgnutls - version >= $min_libgnutls_version) + no_libgnutls="" + if test "$LIBGNUTLS_CONFIG" = "no" ; then + no_libgnutls=yes + else + LIBGNUTLS_CFLAGS=`$LIBGNUTLS_CONFIG $libgnutls_config_args --cflags` + LIBGNUTLS_LIBS=`$LIBGNUTLS_CONFIG $libgnutls_config_args --libs` + libgnutls_config_version=`$LIBGNUTLS_CONFIG $libgnutls_config_args --version` + + + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS" + LIBS="$LIBS $LIBGNUTLS_LIBS" +dnl +dnl Now check if the installed libgnutls is sufficiently new. Also sanity +dnl checks the results of libgnutls-config to some extent +dnl + rm -f conf.libgnutlstest + AC_TRY_RUN([ +#include +#include +#include +#include + +int +main () +{ + system ("touch conf.libgnutlstest"); + + if( strcmp( gnutls_check_version(NULL), "$libgnutls_config_version" ) ) + { + printf("\n*** 'libgnutls-config --version' returned %s, but LIBGNUTLS (%s)\n", + "$libgnutls_config_version", gnutls_check_version(NULL) ); + printf("*** was found! If libgnutls-config was correct, then it is best\n"); + printf("*** to remove the old version of LIBGNUTLS. You may also be able to fix the error\n"); + printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); + printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); + printf("*** required on your system.\n"); + printf("*** If libgnutls-config was wrong, set the environment variable LIBGNUTLS_CONFIG\n"); + printf("*** to point to the correct copy of libgnutls-config, and remove the file config.cache\n"); + printf("*** before re-running configure\n"); + } + else if ( strcmp(gnutls_check_version(NULL), LIBGNUTLS_VERSION ) ) + { + printf("\n*** LIBGNUTLS header file (version %s) does not match\n", LIBGNUTLS_VERSION); + printf("*** library (version %s)\n", gnutls_check_version(NULL) ); + } + else + { + if ( gnutls_check_version( "$min_libgnutls_version" ) ) + { + return 0; + } + else + { + printf("no\n*** An old version of LIBGNUTLS (%s) was found.\n", + gnutls_check_version(NULL) ); + printf("*** You need a version of LIBGNUTLS newer than %s. The latest version of\n", + "$min_libgnutls_version" ); + printf("*** LIBGNUTLS is always available from ftp://gnutls.hellug.gr/pub/gnutls.\n"); + printf("*** \n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the libgnutls-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of LIBGNUTLS, but you can also set the LIBGNUTLS_CONFIG environment to point to the\n"); + printf("*** correct copy of libgnutls-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + } + return 1; +} +],, no_libgnutls=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + + if test "x$no_libgnutls" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + if test -f conf.libgnutlstest ; then + : + else + AC_MSG_RESULT(no) + fi + if test "$LIBGNUTLS_CONFIG" = "no" ; then + echo "*** The libgnutls-config script installed by LIBGNUTLS could not be found" + echo "*** If LIBGNUTLS was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the LIBGNUTLS_CONFIG environment variable to the" + echo "*** full path to libgnutls-config." + else + if test -f conf.libgnutlstest ; then + : + else + echo "*** Could not run libgnutls test program, checking why..." + CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS" + LIBS="$LIBS $LIBGNUTLS_LIBS" + AC_TRY_LINK([ +#include +#include +#include +#include +], [ return !!gnutls_check_version(NULL); ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding LIBGNUTLS or finding the wrong" + echo "*** version of LIBGNUTLS. If it is not finding LIBGNUTLS, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" + echo "***" ], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means LIBGNUTLS was incorrectly installed" + echo "*** or that you have moved LIBGNUTLS since it was installed. In the latter case, you" + echo "*** may want to edit the libgnutls-config script: $LIBGNUTLS_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + LIBGNUTLS_CFLAGS="" + LIBGNUTLS_LIBS="" + ifelse([$3], , :, [$3]) + fi + rm -f conf.libgnutlstest + AC_SUBST(LIBGNUTLS_CFLAGS) + AC_SUBST(LIBGNUTLS_LIBS) +]) + +dnl *-*wedit:notab*-* Please keep this as the last line. diff --git a/src/exception.cpp b/src/exception.cpp index f2106e89..9620a310 100644 --- a/src/exception.cpp +++ b/src/exception.cpp @@ -719,6 +719,60 @@ const char* no_auth_information::name() const throw() { return "no_auth_informat #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + + +// +// tls_exception +// + +tls_exception::~tls_exception() throw() {} +tls_exception::tls_exception(const string& what, const exception& other) + : exception(what, other) {} + +exception* tls_exception::clone() const { return new tls_exception(*this); } +const char* tls_exception::name() const throw() { return "tls_exception"; } + + +// +// certificate_exception +// + +certificate_exception::~certificate_exception() throw() {} +certificate_exception::certificate_exception(const string& what, const exception& other) + : tls_exception(what, other) {} + +exception* certificate_exception::clone() const { return new certificate_exception(*this); } +const char* certificate_exception::name() const throw() { return "certificate_exception"; } + + +// +// certificate_verification_exception +// + +certificate_verification_exception::~certificate_verification_exception() throw() {} +certificate_verification_exception::certificate_verification_exception(const string& what, const exception& other) + : certificate_exception(what, other) {} + +exception* certificate_verification_exception::clone() const { return new certificate_verification_exception(*this); } +const char* certificate_verification_exception::name() const throw() { return "certificate_verification_exception"; } + + +// +// unsupported_certificate_type +// + +unsupported_certificate_type::~unsupported_certificate_type() throw() {} +unsupported_certificate_type::unsupported_certificate_type(const string& type, const exception& other) + : certificate_exception("Unsupported certificate type: '" + type + "'", other) {} + +exception* unsupported_certificate_type::clone() const { return new unsupported_certificate_type(*this); } +const char* unsupported_certificate_type::name() const throw() { return "unsupported_certificate_type"; } + + +#endif // VMIME_HAVE_TLS_SUPPORT + + } // exceptions diff --git a/src/net/authHelper.cpp b/src/net/authHelper.cpp deleted file mode 100644 index e9c217f3..00000000 --- a/src/net/authHelper.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// -// VMime library (http://www.vmime.org) -// Copyright (C) 2002-2005 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 2 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 along -// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.. -// - -#include "vmime/net/authHelper.hpp" - -#include "vmime/config.hpp" -#include "vmime/security/digest/messageDigestFactory.hpp" - - -namespace vmime { -namespace net { - - -// -// This code is based on the Sample Code published in the Appendix of -// the RFC-2104: "HMAC: Keyed-Hashing for Message Authentication". -// - -void hmac_md5(const string& text, const string& key, string& hexDigest) -{ - vmime_uint8 digest[16]; - - unsigned char ipad[65]; // inner padding - key XORd with ipad - unsigned char opad[65]; // outer padding - key XORd with opad - - unsigned char tkey[16]; - int tkeyLen; - - ref md5 = - security::digest::messageDigestFactory::getInstance()->create("md5"); - - // If key is longer than 64 bytes reset it to key = MD5(key) - if (key.length() > 64) - { - md5->reset(); - md5->update(reinterpret_cast (key.data()), key.length()); - md5->finalize(); - - std::copy(md5->getDigest(), md5->getDigest() + 16, tkey); - tkeyLen = 16; - } - else - { - std::copy(key.begin(), key.end(), tkey); - tkeyLen = key.length(); - } - - // - // the HMAC_MD5 transform looks like: - // - // MD5(K XOR opad, MD5(K XOR ipad, text)) - // - // where K is an n byte key - // ipad is the byte 0x36 repeated 64 times - // - // opad is the byte 0x5c repeated 64 times - // and text is the data being protected - // - - // Start out by storing key in pads - std::fill(ipad, ipad + sizeof(ipad), 0); - std::fill(opad, opad + sizeof(opad), 0); - - std::copy(tkey, tkey + tkeyLen, ipad); - std::copy(tkey, tkey + tkeyLen, opad); - - // XOR key with ipad and opad values - for (int i = 0 ; i < 64 ; ++i) - { - ipad[i] ^= 0x36; - opad[i] ^= 0x5c; - } - - // Perform inner MD5 - md5->reset(); - md5->update(ipad, 64); - md5->update(text); - md5->finalize(); - - std::copy(md5->getDigest(), md5->getDigest() + 16, digest); - - // Perform outer MD5 - md5->reset(); - md5->update(opad, 64); - md5->update(digest, 16); - md5->finalize(); - - //std::copy(outerMD5.hash(), outerMD5.hash() + 16, digest); - - hexDigest = md5->getHexDigest(); -} - - -} // net -} // vmime diff --git a/src/net/builtinServices.inl b/src/net/builtinServices.inl index 88578e4b..803371ac 100644 --- a/src/net/builtinServices.inl +++ b/src/net/builtinServices.inl @@ -24,36 +24,52 @@ #ifndef VMIME_BUILDING_DOC -#define REGISTER_SERVICE(p_class, p_name) \ - vmime::net::service::initializer p_name(#p_name) +#define REGISTER_SERVICE(p_class, p_name, p_type) \ + vmime::net::service::initializer \ + p_name(#p_name, vmime::net::service::p_type) #if VMIME_BUILTIN_MESSAGING_PROTO_POP3 #include "vmime/net/pop3/POP3Store.hpp" - REGISTER_SERVICE(pop3::POP3Store, pop3); + REGISTER_SERVICE(pop3::POP3Store, pop3, TYPE_STORE); + + #if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/pop3/POP3SStore.hpp" + REGISTER_SERVICE(pop3::POP3SStore, pop3s, TYPE_STORE); + #endif // VMIME_HAVE_TLS_SUPPORT #endif #if VMIME_BUILTIN_MESSAGING_PROTO_SMTP #include "vmime/net/smtp/SMTPTransport.hpp" - REGISTER_SERVICE(smtp::SMTPTransport, smtp); + REGISTER_SERVICE(smtp::SMTPTransport, smtp, TYPE_TRANSPORT); + + #if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/smtp/SMTPSTransport.hpp" + REGISTER_SERVICE(smtp::SMTPSTransport, smtps, TYPE_TRANSPORT); + #endif // VMIME_HAVE_TLS_SUPPORT #endif #if VMIME_BUILTIN_MESSAGING_PROTO_IMAP #include "vmime/net/imap/IMAPStore.hpp" - REGISTER_SERVICE(imap::IMAPStore, imap); + REGISTER_SERVICE(imap::IMAPStore, imap, TYPE_STORE); + + #if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/imap/IMAPSStore.hpp" + REGISTER_SERVICE(imap::IMAPSStore, imaps, TYPE_STORE); + #endif // VMIME_HAVE_TLS_SUPPORT #endif #if VMIME_BUILTIN_MESSAGING_PROTO_MAILDIR #include "vmime/net/maildir/maildirStore.hpp" - REGISTER_SERVICE(maildir::maildirStore, maildir); + REGISTER_SERVICE(maildir::maildirStore, maildir, TYPE_STORE); #endif #if VMIME_BUILTIN_MESSAGING_PROTO_SENDMAIL #include "vmime/net/sendmail/sendmailTransport.hpp" - REGISTER_SERVICE(sendmail::sendmailTransport, sendmail); + REGISTER_SERVICE(sendmail::sendmailTransport, sendmail, TYPE_TRANSPORT); #endif diff --git a/src/net/imap/IMAPConnection.cpp b/src/net/imap/IMAPConnection.cpp index 6d2a4a4e..b0716f45 100644 --- a/src/net/imap/IMAPConnection.cpp +++ b/src/net/imap/IMAPConnection.cpp @@ -29,16 +29,20 @@ #include "vmime/security/sasl/SASLContext.hpp" #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/tls/TLSSession.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + #include // Helpers for service properties #define GET_PROPERTY(type, prop) \ (m_store->getInfos().getPropertyValue (getSession(), \ - dynamic_cast (m_store->getInfos()).getProperties().prop)) + dynamic_cast (m_store->getInfos()).getProperties().prop)) #define HAS_PROPERTY(prop) \ (m_store->getInfos().hasProperty(getSession(), \ - dynamic_cast (m_store->getInfos()).getProperties().prop)) + dynamic_cast (m_store->getInfos()).getProperties().prop)) namespace vmime { @@ -94,6 +98,20 @@ void IMAPConnection::connect() getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY)); m_socket = sf->create(); + +#if VMIME_HAVE_TLS_SUPPORT + if (m_store->isSecuredConnection()) // dedicated port/IMAPS + { + ref tlsSession = + vmime::create (m_store->getCertificateVerifier()); + + ref tlsSocket = + tlsSession->getSocket(m_socket); + + m_socket = tlsSocket; + } +#endif // VMIME_HAVE_TLS_SUPPORT + m_socket->connect(address, port); @@ -110,6 +128,7 @@ void IMAPConnection::connect() // --- S: * OK mydomain.org IMAP4rev1 v12.256 server ready utility::auto_ptr greet(m_parser->readGreeting()); + bool needAuth = false; if (greet->resp_cond_bye()) { @@ -117,6 +136,47 @@ void IMAPConnection::connect() throw exceptions::connection_greeting_error(m_parser->lastLine()); } else if (greet->resp_cond_auth()->condition() != IMAPParser::resp_cond_auth::PREAUTH) + { + needAuth = true; + } + +#if VMIME_HAVE_TLS_SUPPORT + // Setup secured connection, if requested + const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS); + const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED); + + if (!m_store->isSecuredConnection() && tls) // only if not IMAPS + { + try + { + startTLS(); + } + // Non-fatal error + catch (exceptions::command_error&) + { + if (tlsRequired) + { + m_state = STATE_NONE; + throw; + } + else + { + // TLS is not required, so don't bother + } + } + // Fatal error + catch (...) + { + m_state = STATE_NONE; + throw; + } + } +#endif // VMIME_HAVE_TLS_SUPPORT + + // Authentication + if (needAuth) { try { @@ -287,17 +347,19 @@ void IMAPConnection::authenticateSASL() respDataList = resp->continue_req_or_response_data(); string response; + bool hasResponse = false; for (unsigned int i = 0 ; i < respDataList.size() ; ++i) { if (respDataList[i]->continue_req()) { response = respDataList[i]->continue_req()->resp_text()->text(); + hasResponse = true; break; } } - if (response.empty()) + if (!hasResponse) { cont = false; continue; @@ -365,6 +427,50 @@ void IMAPConnection::authenticateSASL() #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + +void IMAPConnection::startTLS() +{ + try + { + send(true, "STARTTLS", true); + + utility::auto_ptr resp(m_parser->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error + ("STARTTLS", m_parser->lastLine(), "bad response"); + } + + ref tlsSession = + vmime::create (m_store->getCertificateVerifier()); + + ref tlsSocket = + tlsSession->getSocket(m_socket); + + tlsSocket->handshake(m_timeoutHandler); + + m_socket = tlsSocket; + m_parser->setSocket(m_socket); + } + catch (exceptions::command_error&) + { + // Non-fatal error + throw; + } + catch (exception&) + { + // Fatal error + internalDisconnect(); + throw; + } +} + +#endif // VMIME_HAVE_TLS_SUPPORT + + const std::vector IMAPConnection::getCapabilities() { send(true, "CAPABILITY", true); diff --git a/src/net/imap/IMAPSStore.cpp b/src/net/imap/IMAPSStore.cpp new file mode 100644 index 00000000..ed637c43 --- /dev/null +++ b/src/net/imap/IMAPSStore.cpp @@ -0,0 +1,65 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 along +// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.. +// + +#include "vmime/net/imap/IMAPSStore.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPSStore::IMAPSStore(ref sess, ref auth) + : IMAPStore(sess, auth, true) +{ +} + + +IMAPSStore::~IMAPSStore() +{ +} + + +const string IMAPSStore::getProtocolName() const +{ + return "imaps"; +} + + + +// Service infos + +IMAPServiceInfos IMAPSStore::sm_infos(true); + + +const serviceInfos& IMAPSStore::getInfosInstance() +{ + return sm_infos; +} + + +const serviceInfos& IMAPSStore::getInfos() const +{ + return sm_infos; +} + + +} // imap +} // net +} // vmime diff --git a/src/net/imap/IMAPServiceInfos.cpp b/src/net/imap/IMAPServiceInfos.cpp new file mode 100644 index 00000000..d8164ce1 --- /dev/null +++ b/src/net/imap/IMAPServiceInfos.cpp @@ -0,0 +1,133 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 along +// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.. +// + +#include "vmime/net/imap/IMAPServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +IMAPServiceInfos::IMAPServiceInfos(const bool imaps) + : m_imaps(imaps) +{ +} + + +const string IMAPServiceInfos::getPropertyPrefix() const +{ + if (m_imaps) + return "store.imaps."; + else + return "store.imap."; +} + + +const IMAPServiceInfos::props& IMAPServiceInfos::getProperties() const +{ + static props imapProps = + { + // IMAP-specific options +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"), +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "143"), + property(serviceInfos::property::SERVER_SOCKETFACTORY), + + property(serviceInfos::property::TIMEOUT_FACTORY) + }; + + static props imapsProps = + { + // IMAP-specific options +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"), +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "993"), + property(serviceInfos::property::SERVER_SOCKETFACTORY), + + property(serviceInfos::property::TIMEOUT_FACTORY) + }; + + return m_imaps ? imapsProps : imapProps; +} + + +const std::vector IMAPServiceInfos::getAvailableProperties() const +{ + std::vector list; + const props& p = getProperties(); + + // IMAP-specific options +#if VMIME_HAVE_SASL_SUPPORT + list.push_back(p.PROPERTY_OPTIONS_SASL); + list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + list.push_back(p.PROPERTY_AUTH_USERNAME); + list.push_back(p.PROPERTY_AUTH_PASSWORD); + +#if VMIME_HAVE_TLS_SUPPORT + if (!m_imaps) + { + list.push_back(p.PROPERTY_CONNECTION_TLS); + list.push_back(p.PROPERTY_CONNECTION_TLS_REQUIRED); + } +#endif // VMIME_HAVE_TLS_SUPPORT + + list.push_back(p.PROPERTY_SERVER_ADDRESS); + list.push_back(p.PROPERTY_SERVER_PORT); + list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY); + + list.push_back(p.PROPERTY_TIMEOUT_FACTORY); + + return list; +} + + +} // imap +} // net +} // vmime + diff --git a/src/net/imap/IMAPStore.cpp b/src/net/imap/IMAPStore.cpp index d4f18e7c..95f96276 100644 --- a/src/net/imap/IMAPStore.cpp +++ b/src/net/imap/IMAPStore.cpp @@ -32,8 +32,8 @@ namespace net { namespace imap { -IMAPStore::IMAPStore(ref sess, ref auth) - : store(sess, getInfosInstance(), auth), m_connection(NULL) +IMAPStore::IMAPStore(ref sess, ref auth, const bool secured) + : store(sess, getInfosInstance(), auth), m_connection(NULL), m_secured(secured) { } @@ -91,6 +91,12 @@ const bool IMAPStore::isValidFolderName(const folder::path::component& /* name * } +const bool IMAPStore::isSecuredConnection() const +{ + return m_secured; +} + + void IMAPStore::connect() { if (isConnected()) @@ -189,78 +195,21 @@ const int IMAPStore::getCapabilities() const // Service infos -IMAPStore::_infos IMAPStore::sm_infos; +IMAPServiceInfos IMAPStore::sm_infos(false); const serviceInfos& IMAPStore::getInfosInstance() { - return (sm_infos); + return sm_infos; } const serviceInfos& IMAPStore::getInfos() const { - return (sm_infos); + return sm_infos; } -const string IMAPStore::_infos::getPropertyPrefix() const -{ - return "store.imap."; -} - - -const IMAPStore::_infos::props& IMAPStore::_infos::getProperties() const -{ - static props p = - { - // IMAP-specific options -#if VMIME_HAVE_SASL_SUPPORT - property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"), - property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"), -#endif // VMIME_HAVE_SASL_SUPPORT - - // Common properties - property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), - property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), - - property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), - property(serviceInfos::property::SERVER_PORT, "143"), - property(serviceInfos::property::SERVER_SOCKETFACTORY), - - property(serviceInfos::property::TIMEOUT_FACTORY) - }; - - return p; -} - - -const std::vector IMAPStore::_infos::getAvailableProperties() const -{ - std::vector list; - const props& p = getProperties(); - - // IMAP-specific options -#if VMIME_HAVE_SASL_SUPPORT - list.push_back(p.PROPERTY_OPTIONS_SASL); - list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); -#endif // VMIME_HAVE_SASL_SUPPORT - - // Common properties - list.push_back(p.PROPERTY_AUTH_USERNAME); - list.push_back(p.PROPERTY_AUTH_PASSWORD); - - list.push_back(p.PROPERTY_SERVER_ADDRESS); - list.push_back(p.PROPERTY_SERVER_PORT); - list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY); - - list.push_back(p.PROPERTY_TIMEOUT_FACTORY); - - return (list); -} - - - } // imap } // net } // vmime diff --git a/src/net/maildir/maildirServiceInfos.cpp b/src/net/maildir/maildirServiceInfos.cpp new file mode 100644 index 00000000..f6487103 --- /dev/null +++ b/src/net/maildir/maildirServiceInfos.cpp @@ -0,0 +1,64 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 along +// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.. +// + +#include "vmime/net/maildir/maildirServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +maildirServiceInfos::maildirServiceInfos() +{ +} + + +const string maildirServiceInfos::getPropertyPrefix() const +{ + return "store.maildir."; +} + + +const maildirServiceInfos::props& maildirServiceInfos::getProperties() const +{ + static props maildirProps = + { + property(serviceInfos::property::SERVER_ROOTPATH, serviceInfos::property::FLAG_REQUIRED) + }; + + return maildirProps; +} + + +const std::vector maildirServiceInfos::getAvailableProperties() const +{ + std::vector list; + const props& p = getProperties(); + + list.push_back(p.PROPERTY_SERVER_ROOTPATH); + + return list; +} + + +} // maildir +} // net +} // vmime + diff --git a/src/net/maildir/maildirStore.cpp b/src/net/maildir/maildirStore.cpp index bbb95624..d1baf8dd 100644 --- a/src/net/maildir/maildirStore.cpp +++ b/src/net/maildir/maildirStore.cpp @@ -29,9 +29,11 @@ // Helpers for service properties #define GET_PROPERTY(type, prop) \ - (sm_infos.getPropertyValue (getSession(), sm_infos.getProperties().prop)) + (getInfos().getPropertyValue (getSession(), \ + dynamic_cast (getInfos()).getProperties().prop)) #define HAS_PROPERTY(prop) \ - (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop)) + (getInfos().hasProperty(getSession(), \ + dynamic_cast (getInfos()).getProperties().prop)) namespace vmime { @@ -205,50 +207,20 @@ const int maildirStore::getCapabilities() const - // Service infos -maildirStore::_infos maildirStore::sm_infos; +maildirServiceInfos maildirStore::sm_infos; const serviceInfos& maildirStore::getInfosInstance() { - return (sm_infos); + return sm_infos; } const serviceInfos& maildirStore::getInfos() const { - return (sm_infos); -} - - -const string maildirStore::_infos::getPropertyPrefix() const -{ - return "store.maildir."; -} - - -const maildirStore::_infos::props& maildirStore::_infos::getProperties() const -{ - static props p = - { - property(serviceInfos::property::SERVER_ROOTPATH, serviceInfos::property::FLAG_REQUIRED) - }; - - return p; -} - - -const std::vector maildirStore::_infos::getAvailableProperties() const -{ - std::vector list; - const props& p = getProperties(); - - // Maildir-specific properties - list.push_back(p.PROPERTY_SERVER_ROOTPATH); - - return (list); + return sm_infos; } diff --git a/src/net/pop3/POP3SStore.cpp b/src/net/pop3/POP3SStore.cpp new file mode 100644 index 00000000..760ddd86 --- /dev/null +++ b/src/net/pop3/POP3SStore.cpp @@ -0,0 +1,66 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 along +// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.. +// + +#include "vmime/net/pop3/POP3SStore.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3SStore::POP3SStore(ref sess, ref auth) + : POP3Store(sess, auth, true) +{ +} + + +POP3SStore::~POP3SStore() +{ +} + + +const string POP3SStore::getProtocolName() const +{ + return "pop3s"; +} + + + +// Service infos + +POP3ServiceInfos POP3SStore::sm_infos(true); + + +const serviceInfos& POP3SStore::getInfosInstance() +{ + return sm_infos; +} + + +const serviceInfos& POP3SStore::getInfos() const +{ + return sm_infos; +} + + +} // pop3 +} // net +} // vmime + diff --git a/src/net/pop3/POP3ServiceInfos.cpp b/src/net/pop3/POP3ServiceInfos.cpp new file mode 100644 index 00000000..52387c5d --- /dev/null +++ b/src/net/pop3/POP3ServiceInfos.cpp @@ -0,0 +1,139 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 along +// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.. +// + +#include "vmime/net/pop3/POP3ServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +POP3ServiceInfos::POP3ServiceInfos(const bool pop3s) + : m_pop3s(pop3s) +{ +} + + +const string POP3ServiceInfos::getPropertyPrefix() const +{ + if (m_pop3s) + return "store.pop3s."; + else + return "store.pop3."; +} + + +const POP3ServiceInfos::props& POP3ServiceInfos::getProperties() const +{ + static props pop3Props = + { + // POP3-specific options + property("options.apop", serviceInfos::property::TYPE_BOOL, "true"), + property("options.apop.fallback", serviceInfos::property::TYPE_BOOL, "true"), +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"), +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "110"), + property(serviceInfos::property::SERVER_SOCKETFACTORY), + + property(serviceInfos::property::TIMEOUT_FACTORY) + }; + + static props pop3sProps = + { + // POP3-specific options + property("options.apop", serviceInfos::property::TYPE_BOOL, "true"), + property("options.apop.fallback", serviceInfos::property::TYPE_BOOL, "true"), +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"), +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "995"), + property(serviceInfos::property::SERVER_SOCKETFACTORY), + + property(serviceInfos::property::TIMEOUT_FACTORY) + }; + + return m_pop3s ? pop3sProps : pop3Props; +} + + +const std::vector POP3ServiceInfos::getAvailableProperties() const +{ + std::vector list; + const props& p = getProperties(); + + // POP3-specific options + list.push_back(p.PROPERTY_OPTIONS_APOP); + list.push_back(p.PROPERTY_OPTIONS_APOP_FALLBACK); +#if VMIME_HAVE_SASL_SUPPORT + list.push_back(p.PROPERTY_OPTIONS_SASL); + list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + list.push_back(p.PROPERTY_AUTH_USERNAME); + list.push_back(p.PROPERTY_AUTH_PASSWORD); + +#if VMIME_HAVE_TLS_SUPPORT + if (!m_pop3s) + { + list.push_back(p.PROPERTY_CONNECTION_TLS); + list.push_back(p.PROPERTY_CONNECTION_TLS_REQUIRED); + } +#endif // VMIME_HAVE_TLS_SUPPORT + + list.push_back(p.PROPERTY_SERVER_ADDRESS); + list.push_back(p.PROPERTY_SERVER_PORT); + list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY); + + list.push_back(p.PROPERTY_TIMEOUT_FACTORY); + + return list; +} + + +} // pop3 +} // net +} // vmime + diff --git a/src/net/pop3/POP3Store.cpp b/src/net/pop3/POP3Store.cpp index cabd81f0..4f373268 100644 --- a/src/net/pop3/POP3Store.cpp +++ b/src/net/pop3/POP3Store.cpp @@ -31,14 +31,20 @@ #include "vmime/security/sasl/SASLContext.hpp" #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/tls/TLSSession.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + #include // Helpers for service properties #define GET_PROPERTY(type, prop) \ - (sm_infos.getPropertyValue (getSession(), sm_infos.getProperties().prop)) + (getInfos().getPropertyValue (getSession(), \ + dynamic_cast (getInfos()).getProperties().prop)) #define HAS_PROPERTY(prop) \ - (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop)) + (getInfos().hasProperty(getSession(), \ + dynamic_cast (getInfos()).getProperties().prop)) namespace vmime { @@ -46,9 +52,9 @@ namespace net { namespace pop3 { -POP3Store::POP3Store(ref sess, ref auth) +POP3Store::POP3Store(ref sess, ref auth, const bool secured) : store(sess, getInfosInstance(), auth), m_socket(NULL), - m_authentified(false), m_timeoutHandler(NULL) + m_authentified(false), m_timeoutHandler(NULL), m_secured(secured) { } @@ -130,6 +136,20 @@ void POP3Store::connect() getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY)); m_socket = sf->create(); + +#if VMIME_HAVE_TLS_SUPPORT + if (m_secured) // dedicated port/POP3S + { + ref tlsSession = + vmime::create (getCertificateVerifier()); + + ref tlsSocket = + tlsSession->getSocket(m_socket); + + m_socket = tlsSocket; + } +#endif // VMIME_HAVE_TLS_SUPPORT + m_socket->connect(address, port); // Connection @@ -146,6 +166,39 @@ void POP3Store::connect() throw exceptions::connection_greeting_error(response); } +#if VMIME_HAVE_TLS_SUPPORT + // Setup secured connection, if requested + const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS); + const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED); + + if (!m_secured && tls) // only if not POP3S + { + try + { + startTLS(); + } + // Non-fatal error + catch (exceptions::command_error&) + { + if (tlsRequired) + { + throw; + } + else + { + // TLS is not required, so don't bother + } + } + // Fatal error + catch (...) + { + throw; + } + } +#endif // VMIME_HAVE_TLS_SUPPORT + // Start authentication process authenticate(messageId(response)); } @@ -235,6 +288,18 @@ void POP3Store::authenticate(const messageId& randomMID) internalDisconnect(); throw exceptions::authentication_error(response); } + + // Ensure connection is valid (cf. note above) + try + { + string response2; + sendRequest("NOOP"); + readResponse(response2, false); + } + catch (exceptions::socket_exception&) + { + throw exceptions::authentication_error(response); + } } } else @@ -453,6 +518,46 @@ void POP3Store::authenticateSASL() #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + +void POP3Store::startTLS() +{ + try + { + sendRequest("STLS"); + + string response; + readResponse(response, false); + + if (getResponseCode(response) != RESPONSE_OK) + throw exceptions::command_error("STLS", response); + + ref tlsSession = + vmime::create (getCertificateVerifier()); + + ref tlsSocket = + tlsSession->getSocket(m_socket); + + tlsSocket->handshake(m_timeoutHandler); + + m_socket = tlsSocket; + } + catch (exceptions::command_error&) + { + // Non-fatal error + throw; + } + catch (exception&) + { + // Fatal error + internalDisconnect(); + throw; + } +} + +#endif // VMIME_HAVE_TLS_SUPPORT + + const bool POP3Store::isConnected() const { return (m_socket && m_socket->isConnected() && m_authentified); @@ -478,8 +583,14 @@ void POP3Store::internalDisconnect() m_folders.clear(); - - sendRequest("QUIT"); + try + { + sendRequest("QUIT"); + } + catch (exception&) + { + // Not important + } m_socket->disconnect(); m_socket = NULL; @@ -627,6 +738,8 @@ void POP3Store::readResponse(string& buffer, const bool multiLine, { if (!m_timeoutHandler->handleTimeOut()) throw exceptions::operation_timed_out(); + + m_timeoutHandler->resetTimeOut(); } // Receive data from the socket @@ -844,78 +957,18 @@ const int POP3Store::getCapabilities() const // Service infos -POP3Store::_infos POP3Store::sm_infos; +POP3ServiceInfos POP3Store::sm_infos(false); const serviceInfos& POP3Store::getInfosInstance() { - return (sm_infos); + return sm_infos; } const serviceInfos& POP3Store::getInfos() const { - return (sm_infos); -} - - -const string POP3Store::_infos::getPropertyPrefix() const -{ - return "store.pop3."; -} - - -const POP3Store::_infos::props& POP3Store::_infos::getProperties() const -{ - static props p = - { - // POP3-specific options - property("options.apop", serviceInfos::property::TYPE_BOOL, "true"), - property("options.apop.fallback", serviceInfos::property::TYPE_BOOL, "true"), -#if VMIME_HAVE_SASL_SUPPORT - property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"), - property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "true"), -#endif // VMIME_HAVE_SASL_SUPPORT - - // Common properties - property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), - property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), - - property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), - property(serviceInfos::property::SERVER_PORT, "110"), - property(serviceInfos::property::SERVER_SOCKETFACTORY), - - property(serviceInfos::property::TIMEOUT_FACTORY) - }; - - return p; -} - - -const std::vector POP3Store::_infos::getAvailableProperties() const -{ - std::vector list; - const props& p = getProperties(); - - // POP3-specific options - list.push_back(p.PROPERTY_OPTIONS_APOP); - list.push_back(p.PROPERTY_OPTIONS_APOP_FALLBACK); -#if VMIME_HAVE_SASL_SUPPORT - list.push_back(p.PROPERTY_OPTIONS_SASL); - list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); -#endif // VMIME_HAVE_SASL_SUPPORT - - // Common properties - list.push_back(p.PROPERTY_AUTH_USERNAME); - list.push_back(p.PROPERTY_AUTH_PASSWORD); - - list.push_back(p.PROPERTY_SERVER_ADDRESS); - list.push_back(p.PROPERTY_SERVER_PORT); - list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY); - - list.push_back(p.PROPERTY_TIMEOUT_FACTORY); - - return (list); + return sm_infos; } diff --git a/src/net/sendmail/sendmailServiceInfos.cpp b/src/net/sendmail/sendmailServiceInfos.cpp new file mode 100644 index 00000000..f0f2fa59 --- /dev/null +++ b/src/net/sendmail/sendmailServiceInfos.cpp @@ -0,0 +1,65 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 along +// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.. +// + +#include "vmime/net/sendmail/sendmailServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace sendmail { + + +sendmailServiceInfos::sendmailServiceInfos() +{ +} + + +const string sendmailServiceInfos::getPropertyPrefix() const +{ + return "transport.sendmail."; +} + + +const sendmailServiceInfos::props& sendmailServiceInfos::getProperties() const +{ + static props sendmailProps = + { + // Path to sendmail (override default) + property("binpath", serviceInfos::property::TYPE_STRING, string(VMIME_SENDMAIL_PATH)) + }; + + return sendmailProps; +} + + +const std::vector sendmailServiceInfos::getAvailableProperties() const +{ + std::vector list; + const props& p = getProperties(); + + list.push_back(p.PROPERTY_BINPATH); + + return list; +} + + +} // sendmail +} // net +} // vmime + diff --git a/src/net/sendmail/sendmailTransport.cpp b/src/net/sendmail/sendmailTransport.cpp index 4a077465..853f4eec 100644 --- a/src/net/sendmail/sendmailTransport.cpp +++ b/src/net/sendmail/sendmailTransport.cpp @@ -33,9 +33,11 @@ // Helpers for service properties #define GET_PROPERTY(type, prop) \ - (sm_infos.getPropertyValue (getSession(), sm_infos.getProperties().prop)) + (getInfos().getPropertyValue (getSession(), \ + dynamic_cast (getInfos()).getProperties().prop)) #define HAS_PROPERTY(prop) \ - (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop)) + (getInfos().hasProperty(getSession(), \ + dynamic_cast (getInfos()).getProperties().prop)) #if VMIME_BUILTIN_PLATFORM_POSIX @@ -177,47 +179,18 @@ void sendmailTransport::internalSend // Service infos -sendmailTransport::_infos sendmailTransport::sm_infos; +sendmailServiceInfos sendmailTransport::sm_infos; const serviceInfos& sendmailTransport::getInfosInstance() { - return (sm_infos); + return sm_infos; } const serviceInfos& sendmailTransport::getInfos() const { - return (sm_infos); -} - - -const string sendmailTransport::_infos::getPropertyPrefix() const -{ - return "transport.sendmail."; -} - - -const sendmailTransport::_infos::props& sendmailTransport::_infos::getProperties() const -{ - static props p = - { - // Path to sendmail (override default) - property("binpath", serviceInfos::property::TYPE_STRING, string(VMIME_SENDMAIL_PATH)) - }; - - return p; -} - - -const std::vector sendmailTransport::_infos::getAvailableProperties() const -{ - std::vector list; - const props& p = getProperties(); - - list.push_back(p.PROPERTY_BINPATH); - - return (list); + return sm_infos; } diff --git a/src/net/service.cpp b/src/net/service.cpp index acdfb390..35e8aa55 100644 --- a/src/net/service.cpp +++ b/src/net/service.cpp @@ -26,6 +26,10 @@ #include "vmime/security/defaultAuthenticator.hpp" #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/tls/defaultCertificateVerifier.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + namespace vmime { namespace net { @@ -45,6 +49,11 @@ service::service(ref sess, const serviceInfos& /* infos */, (); #endif // VMIME_HAVE_SASL_SUPPORT } + +#if VMIME_HAVE_TLS_SUPPORT + m_certVerifier = vmime::create (); +#endif // VMIME_HAVE_TLS_SUPPORT + } @@ -83,5 +92,21 @@ void service::setAuthenticator(ref auth) } +#if VMIME_HAVE_TLS_SUPPORT + +void service::setCertificateVerifier(ref cv) +{ + m_certVerifier = cv; +} + + +ref service::getCertificateVerifier() +{ + return m_certVerifier; +} + +#endif // VMIME_HAVE_TLS_SUPPORT + + } // net } // vmime diff --git a/src/net/serviceInfos.cpp b/src/net/serviceInfos.cpp index 68d78c73..306c1ec3 100644 --- a/src/net/serviceInfos.cpp +++ b/src/net/serviceInfos.cpp @@ -46,6 +46,16 @@ const serviceInfos::property serviceInfos::property::AUTH_PASSWORD const serviceInfos::property serviceInfos::property::TIMEOUT_FACTORY ("timeout.factory", serviceInfos::property::TYPE_STRING); +#if VMIME_HAVE_TLS_SUPPORT + +const serviceInfos::property serviceInfos::property::CONNECTION_TLS + ("connection.tls", serviceInfos::property::TYPE_BOOL, "false"); + +const serviceInfos::property serviceInfos::property::CONNECTION_TLS_REQUIRED + ("connection.tls.required", serviceInfos::property::TYPE_BOOL, "false"); + +#endif // VMIME_HAVE_TLS_SUPPORT + // serviceInfos diff --git a/src/net/smtp/SMTPSTransport.cpp b/src/net/smtp/SMTPSTransport.cpp new file mode 100644 index 00000000..22bf4e4b --- /dev/null +++ b/src/net/smtp/SMTPSTransport.cpp @@ -0,0 +1,66 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 along +// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.. +// + +#include "vmime/net/smtp/SMTPSTransport.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPSTransport::SMTPSTransport(ref sess, ref auth) + : SMTPTransport(sess, auth, true) +{ +} + + +SMTPSTransport::~SMTPSTransport() +{ +} + + +const string SMTPSTransport::getProtocolName() const +{ + return "smtps"; +} + + + +// Service infos + +SMTPServiceInfos SMTPSTransport::sm_infos(true); + + +const serviceInfos& SMTPSTransport::getInfosInstance() +{ + return sm_infos; +} + + +const serviceInfos& SMTPSTransport::getInfos() const +{ + return sm_infos; +} + + +} // smtp +} // net +} // vmime + diff --git a/src/net/smtp/SMTPServiceInfos.cpp b/src/net/smtp/SMTPServiceInfos.cpp new file mode 100644 index 00000000..1f0bca6c --- /dev/null +++ b/src/net/smtp/SMTPServiceInfos.cpp @@ -0,0 +1,136 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 along +// with this program; if not, write to the Free Software Foundation, Inc., Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.. +// + +#include "vmime/net/smtp/SMTPServiceInfos.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +SMTPServiceInfos::SMTPServiceInfos(const bool smtps) + : m_smtps(smtps) +{ +} + + +const string SMTPServiceInfos::getPropertyPrefix() const +{ + if (m_smtps) + return "transport.smtps."; + else + return "transport.smtp."; +} + + +const SMTPServiceInfos::props& SMTPServiceInfos::getProperties() const +{ + static props smtpProps = + { + // SMTP-specific options + property("options.need-authentication", serviceInfos::property::TYPE_BOOL, "false"), +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "false"), +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "25"), + property(serviceInfos::property::SERVER_SOCKETFACTORY), + + property(serviceInfos::property::TIMEOUT_FACTORY) + }; + + static props smtpsProps = + { + // SMTP-specific options + property("options.need-authentication", serviceInfos::property::TYPE_BOOL, "false"), +#if VMIME_HAVE_SASL_SUPPORT + property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"), + property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "false"), +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + property(serviceInfos::property::AUTH_USERNAME, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::AUTH_PASSWORD, serviceInfos::property::FLAG_REQUIRED), + +#if VMIME_HAVE_TLS_SUPPORT + property(serviceInfos::property::CONNECTION_TLS), + property(serviceInfos::property::CONNECTION_TLS_REQUIRED), +#endif // VMIME_HAVE_TLS_SUPPORT + + property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), + property(serviceInfos::property::SERVER_PORT, "465"), + property(serviceInfos::property::SERVER_SOCKETFACTORY), + + property(serviceInfos::property::TIMEOUT_FACTORY) + }; + + return m_smtps ? smtpsProps : smtpProps; +} + + +const std::vector SMTPServiceInfos::getAvailableProperties() const +{ + std::vector list; + const props& p = getProperties(); + + // SMTP-specific options + list.push_back(p.PROPERTY_OPTIONS_NEEDAUTH); +#if VMIME_HAVE_SASL_SUPPORT + list.push_back(p.PROPERTY_OPTIONS_SASL); + list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + list.push_back(p.PROPERTY_AUTH_USERNAME); + list.push_back(p.PROPERTY_AUTH_PASSWORD); + +#if VMIME_HAVE_TLS_SUPPORT + if (!m_smtps) + { + list.push_back(p.PROPERTY_CONNECTION_TLS); + list.push_back(p.PROPERTY_CONNECTION_TLS_REQUIRED); + } +#endif // VMIME_HAVE_TLS_SUPPORT + + list.push_back(p.PROPERTY_SERVER_ADDRESS); + list.push_back(p.PROPERTY_SERVER_PORT); + list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY); + + list.push_back(p.PROPERTY_TIMEOUT_FACTORY); + + return list; +} + + +} // smtp +} // net +} // vmime + diff --git a/src/net/smtp/SMTPTransport.cpp b/src/net/smtp/SMTPTransport.cpp index 1aa15c13..d0c72a13 100644 --- a/src/net/smtp/SMTPTransport.cpp +++ b/src/net/smtp/SMTPTransport.cpp @@ -24,8 +24,6 @@ #include "vmime/encoderB64.hpp" #include "vmime/mailboxList.hpp" -#include "vmime/net/authHelper.hpp" - #include "vmime/utility/filteredStream.hpp" #include "vmime/utility/stringUtils.hpp" @@ -33,12 +31,18 @@ #include "vmime/security/sasl/SASLContext.hpp" #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/tls/TLSSession.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + // Helpers for service properties #define GET_PROPERTY(type, prop) \ - (sm_infos.getPropertyValue (getSession(), sm_infos.getProperties().prop)) + (getInfos().getPropertyValue (getSession(), \ + dynamic_cast (getInfos()).getProperties().prop)) #define HAS_PROPERTY(prop) \ - (sm_infos.hasProperty(getSession(), sm_infos.getProperties().prop)) + (getInfos().hasProperty(getSession(), \ + dynamic_cast (getInfos()).getProperties().prop)) namespace vmime { @@ -46,9 +50,10 @@ namespace net { namespace smtp { -SMTPTransport::SMTPTransport(ref sess, ref auth) +SMTPTransport::SMTPTransport(ref sess, ref auth, const bool secured) : transport(sess, getInfosInstance(), auth), m_socket(NULL), - m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL) + m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL), + m_secured(secured) { } @@ -97,6 +102,20 @@ void SMTPTransport::connect() getSocketFactory(GET_PROPERTY(string, PROPERTY_SERVER_SOCKETFACTORY)); m_socket = sf->create(); + +#if VMIME_HAVE_TLS_SUPPORT + if (m_secured) // dedicated port/SMTPS + { + ref tlsSession = + vmime::create (getCertificateVerifier()); + + ref tlsSocket = + tlsSession->getSocket(m_socket); + + m_socket = tlsSocket; + } +#endif // VMIME_HAVE_TLS_SUPPORT + m_socket->connect(address, port); m_responseBuffer.clear(); @@ -146,6 +165,39 @@ void SMTPTransport::connect() m_extendedSMTPResponse = response; } +#if VMIME_HAVE_TLS_SUPPORT + // Setup secured connection, if requested + const bool tls = HAS_PROPERTY(PROPERTY_CONNECTION_TLS) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS); + const bool tlsRequired = HAS_PROPERTY(PROPERTY_CONNECTION_TLS_REQUIRED) + && GET_PROPERTY(bool, PROPERTY_CONNECTION_TLS_REQUIRED); + + if (!m_secured && tls) // only if not POP3S + { + try + { + startTLS(); + } + // Non-fatal error + catch (exceptions::command_error&) + { + if (tlsRequired) + { + throw; + } + else + { + // TLS is not required, so don't bother + } + } + // Fatal error + catch (...) + { + throw; + } + } +#endif // VMIME_HAVE_TLS_SUPPORT + // Authentication if (GET_PROPERTY(bool, PROPERTY_OPTIONS_NEEDAUTH)) authenticate(); @@ -369,6 +421,45 @@ void SMTPTransport::authenticateSASL() #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + +void SMTPTransport::startTLS() +{ + try + { + sendRequest("STARTTLS"); + + string response; + + if (readAllResponses(response) != 220) + throw exceptions::command_error("STARTTLS", response); + + ref tlsSession = + vmime::create (getCertificateVerifier()); + + ref tlsSocket = + tlsSession->getSocket(m_socket); + + tlsSocket->handshake(m_timeoutHandler); + + m_socket = tlsSocket; + } + catch (exceptions::command_error&) + { + // Non-fatal error + throw; + } + catch (exception&) + { + // Fatal error + internalDisconnect(); + throw; + } +} + +#endif // VMIME_HAVE_TLS_SUPPORT + + const bool SMTPTransport::isConnected() const { return (m_socket && m_socket->isConnected() && m_authentified); @@ -516,6 +607,8 @@ const string SMTPTransport::readResponseLine() { if (!m_timeoutHandler->handleTimeOut()) throw exceptions::operation_timed_out(); + + m_timeoutHandler->resetTimeOut(); } // Receive data from the socket @@ -589,76 +682,18 @@ const int SMTPTransport::readAllResponses(string& outText, const bool allText) // Service infos -SMTPTransport::_infos SMTPTransport::sm_infos; +SMTPServiceInfos SMTPTransport::sm_infos(false); const serviceInfos& SMTPTransport::getInfosInstance() { - return (sm_infos); + return sm_infos; } const serviceInfos& SMTPTransport::getInfos() const { - return (sm_infos); -} - - -const string SMTPTransport::_infos::getPropertyPrefix() const -{ - return "transport.smtp."; -} - - -const SMTPTransport::_infos::props& SMTPTransport::_infos::getProperties() const -{ - static props p = - { - // SMTP-specific options - property("options.need-authentication", serviceInfos::property::TYPE_BOOL, "false"), -#if VMIME_HAVE_SASL_SUPPORT - property("options.sasl", serviceInfos::property::TYPE_BOOL, "true"), - property("options.sasl.fallback", serviceInfos::property::TYPE_BOOL, "false"), -#endif // VMIME_HAVE_SASL_SUPPORT - - // Common properties - property(serviceInfos::property::AUTH_USERNAME), - property(serviceInfos::property::AUTH_PASSWORD), - - property(serviceInfos::property::SERVER_ADDRESS, serviceInfos::property::FLAG_REQUIRED), - property(serviceInfos::property::SERVER_PORT, "25"), - property(serviceInfos::property::SERVER_SOCKETFACTORY), - - property(serviceInfos::property::TIMEOUT_FACTORY) - }; - - return p; -} - - -const std::vector SMTPTransport::_infos::getAvailableProperties() const -{ - std::vector list; - const props& p = getProperties(); - - // SMTP-specific options - list.push_back(p.PROPERTY_OPTIONS_NEEDAUTH); -#if VMIME_HAVE_SASL_SUPPORT - list.push_back(p.PROPERTY_OPTIONS_SASL); - list.push_back(p.PROPERTY_OPTIONS_SASL_FALLBACK); -#endif // VMIME_HAVE_SASL_SUPPORT - - // Common properties - list.push_back(p.PROPERTY_AUTH_USERNAME); - list.push_back(p.PROPERTY_AUTH_PASSWORD); - - list.push_back(p.PROPERTY_SERVER_ADDRESS); - list.push_back(p.PROPERTY_SERVER_PORT); - list.push_back(p.PROPERTY_SERVER_SOCKETFACTORY); - - list.push_back(p.PROPERTY_TIMEOUT_FACTORY); - - return (list); + return sm_infos; } diff --git a/src/net/tls/TLSSession.cpp b/src/net/tls/TLSSession.cpp new file mode 100644 index 00000000..fb84714c --- /dev/null +++ b/src/net/tls/TLSSession.cpp @@ -0,0 +1,342 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 +#include + +#include "vmime/net/tls/TLSSession.hpp" + +#include "vmime/exception.hpp" + + +// Enable GnuTLS debugging by defining GNUTLS_DEBUG +//#define GNUTLS_DEBUG 1 + + +#if VMIME_DEBUG && GNUTLS_DEBUG + #include +#endif // VMIME_DEBUG && GNUTLS_DEBUG + + +namespace vmime { +namespace net { +namespace tls { + + +#ifndef VMIME_BUILDING_DOC + +// Initialize GNU TLS library +struct TLSGlobal +{ + TLSGlobal() + { + 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. + 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); + + // 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) +{ + string msg = fname + "() returned "; + +#define ERROR(x) \ + case x: msg += #x; break; + + switch (code) + { + ERROR(GNUTLS_E_SUCCESS) + ERROR(GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM) + ERROR(GNUTLS_E_UNKNOWN_CIPHER_TYPE) + ERROR(GNUTLS_E_LARGE_PACKET) + ERROR(GNUTLS_E_UNSUPPORTED_VERSION_PACKET) + ERROR(GNUTLS_E_UNEXPECTED_PACKET_LENGTH) + ERROR(GNUTLS_E_INVALID_SESSION) + ERROR(GNUTLS_E_FATAL_ALERT_RECEIVED) + ERROR(GNUTLS_E_UNEXPECTED_PACKET) + ERROR(GNUTLS_E_WARNING_ALERT_RECEIVED) + ERROR(GNUTLS_E_ERROR_IN_FINISHED_PACKET) + ERROR(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET) + ERROR(GNUTLS_E_UNKNOWN_CIPHER_SUITE) + ERROR(GNUTLS_E_UNWANTED_ALGORITHM) + ERROR(GNUTLS_E_MPI_SCAN_FAILED) + ERROR(GNUTLS_E_DECRYPTION_FAILED) + ERROR(GNUTLS_E_MEMORY_ERROR) + ERROR(GNUTLS_E_DECOMPRESSION_FAILED) + ERROR(GNUTLS_E_COMPRESSION_FAILED) + ERROR(GNUTLS_E_AGAIN) + ERROR(GNUTLS_E_EXPIRED) + ERROR(GNUTLS_E_DB_ERROR) + ERROR(GNUTLS_E_SRP_PWD_ERROR) + ERROR(GNUTLS_E_INSUFFICIENT_CREDENTIALS) + ERROR(GNUTLS_E_HASH_FAILED) + ERROR(GNUTLS_E_BASE64_DECODING_ERROR) + ERROR(GNUTLS_E_MPI_PRINT_FAILED) + ERROR(GNUTLS_E_REHANDSHAKE) + ERROR(GNUTLS_E_GOT_APPLICATION_DATA) + ERROR(GNUTLS_E_RECORD_LIMIT_REACHED) + ERROR(GNUTLS_E_ENCRYPTION_FAILED) + ERROR(GNUTLS_E_PK_ENCRYPTION_FAILED) + ERROR(GNUTLS_E_PK_DECRYPTION_FAILED) + ERROR(GNUTLS_E_PK_SIGN_FAILED) + ERROR(GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION) + ERROR(GNUTLS_E_KEY_USAGE_VIOLATION) + ERROR(GNUTLS_E_NO_CERTIFICATE_FOUND) + ERROR(GNUTLS_E_INVALID_REQUEST) + ERROR(GNUTLS_E_SHORT_MEMORY_BUFFER) + ERROR(GNUTLS_E_INTERRUPTED) + ERROR(GNUTLS_E_PUSH_ERROR) + ERROR(GNUTLS_E_PULL_ERROR) + ERROR(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER) + ERROR(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + ERROR(GNUTLS_E_PKCS1_WRONG_PAD) + ERROR(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION) + ERROR(GNUTLS_E_INTERNAL_ERROR) + ERROR(GNUTLS_E_DH_PRIME_UNACCEPTABLE) + ERROR(GNUTLS_E_FILE_ERROR) + ERROR(GNUTLS_E_TOO_MANY_EMPTY_PACKETS) + ERROR(GNUTLS_E_UNKNOWN_PK_ALGORITHM) + ERROR(GNUTLS_E_INIT_LIBEXTRA) + ERROR(GNUTLS_E_LIBRARY_VERSION_MISMATCH) + ERROR(GNUTLS_E_NO_TEMPORARY_RSA_PARAMS) + ERROR(GNUTLS_E_LZO_INIT_FAILED) + ERROR(GNUTLS_E_NO_COMPRESSION_ALGORITHMS) + ERROR(GNUTLS_E_NO_CIPHER_SUITES) + ERROR(GNUTLS_E_OPENPGP_GETKEY_FAILED) + ERROR(GNUTLS_E_PK_SIG_VERIFY_FAILED) + ERROR(GNUTLS_E_ILLEGAL_SRP_USERNAME) + ERROR(GNUTLS_E_SRP_PWD_PARSING_ERROR) + ERROR(GNUTLS_E_NO_TEMPORARY_DH_PARAMS) + ERROR(GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + ERROR(GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND) + ERROR(GNUTLS_E_ASN1_DER_ERROR) + ERROR(GNUTLS_E_ASN1_VALUE_NOT_FOUND) + ERROR(GNUTLS_E_ASN1_GENERIC_ERROR) + ERROR(GNUTLS_E_ASN1_VALUE_NOT_VALID) + ERROR(GNUTLS_E_ASN1_TAG_ERROR) + ERROR(GNUTLS_E_ASN1_TAG_IMPLICIT) + ERROR(GNUTLS_E_ASN1_TYPE_ANY_ERROR) + ERROR(GNUTLS_E_ASN1_SYNTAX_ERROR) + ERROR(GNUTLS_E_ASN1_DER_OVERFLOW) + ERROR(GNUTLS_E_OPENPGP_TRUSTDB_VERSION_UNSUPPORTED) + ERROR(GNUTLS_E_OPENPGP_UID_REVOKED) + ERROR(GNUTLS_E_CERTIFICATE_ERROR) + //ERROR(GNUTLS_E_X509_CERTIFICATE_ERROR) + ERROR(GNUTLS_E_CERTIFICATE_KEY_MISMATCH) + ERROR(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE) + ERROR(GNUTLS_E_X509_UNKNOWN_SAN) + ERROR(GNUTLS_E_OPENPGP_FINGERPRINT_UNSUPPORTED) + ERROR(GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE) + ERROR(GNUTLS_E_UNKNOWN_HASH_ALGORITHM) + ERROR(GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE) + ERROR(GNUTLS_E_UNKNOWN_PKCS_BAG_TYPE) + ERROR(GNUTLS_E_INVALID_PASSWORD) + ERROR(GNUTLS_E_MAC_VERIFY_FAILED) + ERROR(GNUTLS_E_CONSTRAINT_ERROR) + ERROR(GNUTLS_E_BASE64_ENCODING_ERROR) + ERROR(GNUTLS_E_INCOMPATIBLE_GCRYPT_LIBRARY) + //ERROR(GNUTLS_E_INCOMPATIBLE_CRYPTO_LIBRARY) + ERROR(GNUTLS_E_INCOMPATIBLE_LIBTASN1_LIBRARY) + ERROR(GNUTLS_E_OPENPGP_KEYRING_ERROR) + ERROR(GNUTLS_E_X509_UNSUPPORTED_OID) + //ERROR(GNUTLS_E_RANDOM_FAILED) + ERROR(GNUTLS_E_UNIMPLEMENTED_FEATURE) + + default: + + msg += "unknown error"; + break; + } + +#undef ERROR + + throw exceptions::tls_exception(msg); +} + + +} // tls +} // net +} // vmime + diff --git a/src/net/tls/TLSSocket.cpp b/src/net/tls/TLSSocket.cpp new file mode 100644 index 00000000..ebf3214b --- /dev/null +++ b/src/net/tls/TLSSocket.cpp @@ -0,0 +1,391 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 +#include + +#include "vmime/net/tls/TLSSocket.hpp" +#include "vmime/net/tls/TLSSession.hpp" + +#include "vmime/platformDependant.hpp" + +#include "vmime/net/tls/X509Certificate.hpp" + + +namespace vmime { +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() +{ + 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; + } +} + + +const bool TLSSocket::isConnected() const +{ + return m_wrapped->isConnected() && m_connected; +} + + +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()); +} + + +const int TLSSocket::receiveRaw(char* buffer, const int 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 int 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 + platformDependant::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 + platformDependant::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() +{ + unsigned int certCount = 0; + const gnutls_datum* rawData = gnutls_certificate_get_peers + (*m_session->m_gnutlsSession, &certCount); + + // Try X.509 + gnutls_x509_crt* x509Certs = new gnutls_x509_crt[certCount]; + + unsigned int count = certCount; + + int res = gnutls_x509_crt_list_import + (x509Certs, &count, rawData, GNUTLS_X509_FMT_PEM, 0); + + if (res <= 0) + { + count = certCount; + + res = gnutls_x509_crt_list_import + (x509Certs, &count, rawData, GNUTLS_X509_FMT_DER, 0); + } + + if (res >= 1) + { + std::vector > certs; + bool error = false; + + count = static_cast (res); + + for (unsigned int i = 0 ; i < count ; ++i) + { + size_t dataSize = 0; + + gnutls_x509_crt_export(x509Certs[i], + GNUTLS_X509_FMT_DER, NULL, &dataSize); + + byte* data = new byte[dataSize]; + + gnutls_x509_crt_export(x509Certs[i], + GNUTLS_X509_FMT_DER, data, &dataSize); + + ref cert = + X509Certificate::import(data, dataSize); + + if (cert != NULL) + certs.push_back(cert); + else + error = true; + + delete [] data; + + 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 not catched. + +#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) + { + // To avoid memory leaks + exToDelete.push_back(vmime::create (m_ex)); + + throw *m_ex; + } +} + + +} // tls +} // net +} // vmime + diff --git a/src/net/tls/X509Certificate.cpp b/src/net/tls/X509Certificate.cpp new file mode 100644 index 00000000..cfb52a1d --- /dev/null +++ b/src/net/tls/X509Certificate.cpp @@ -0,0 +1,273 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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 +#include + +#include + +#include "vmime/net/tls/X509Certificate.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +#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* data, const unsigned int length) +{ + ref cert = vmime::create (); + + gnutls_datum buffer; + buffer.data = const_cast (data); + buffer.size = length; + + // Try DER format + if (gnutls_x509_crt_import(cert->m_data->cert, &buffer, GNUTLS_X509_FMT_DER) >= 0) + return cert; + + // Try PEM format + if (gnutls_x509_crt_import(cert->m_data->cert, &buffer, GNUTLS_X509_FMT_PEM) >= 0) + return cert; + + 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); + + byte* data = new byte[dataSize]; + + gnutls_x509_crt_export(m_data->cert, fmt, data, &dataSize); + + try + { + os.write(reinterpret_cast (data), dataSize); + } + catch (...) + { + delete [] data; + throw; + } +} + + +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); +} + + +const bool X509Certificate::checkIssuer + (ref issuer) const +{ + return (gnutls_x509_crt_check_issuer + (m_data->cert, issuer->m_data->cert) >= 1); +} + + +const 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); + + byte* buffer = new byte[bufferSize]; + + if (gnutls_x509_crt_get_fingerprint + (m_data->cert, galgo, buffer, &bufferSize) == 0) + { + byteArray res; + res.insert(res.end(), buffer, buffer + bufferSize); + + delete [] buffer; + + return res; + } + + delete [] buffer; + + 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"; +} + + +const int X509Certificate::getVersion() const +{ + return gnutls_x509_crt_get_version(m_data->cert); +} + + +const 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; +} + + +} // tls +} // net +} // vmime + diff --git a/vmime/net/authHelper.hpp b/src/net/tls/certificateChain.cpp similarity index 74% rename from vmime/net/authHelper.hpp rename to src/net/tls/certificateChain.cpp index 54487fbe..52855cc2 100644 --- a/vmime/net/authHelper.hpp +++ b/src/net/tls/certificateChain.cpp @@ -21,22 +21,33 @@ // the GNU General Public License cover the whole combination. // -#ifndef VMIME_NET_AUTHHELPER_HPP_INCLUDED -#define VMIME_NET_AUTHHELPER_HPP_INCLUDED - - -#include "vmime/types.hpp" +#include "vmime/net/tls/certificateChain.hpp" namespace vmime { namespace net { +namespace tls { -void hmac_md5(const string& text, const string& key, string& hexDigest); +certificateChain::certificateChain(const std::vector >& certs) + : m_certs(certs) +{ +} +const unsigned int certificateChain::getCount() const +{ + return static_cast (m_certs.size()); +} + + +ref certificateChain::getAt(const unsigned int index) +{ + return m_certs[index]; +} + + +} // tls } // net } // vmime - -#endif // VMIME_NET_AUTHHELPER_HPP_INCLUDED diff --git a/src/net/tls/defaultCertificateVerifier.cpp b/src/net/tls/defaultCertificateVerifier.cpp new file mode 100644 index 00000000..de0c6e45 --- /dev/null +++ b/src/net/tls/defaultCertificateVerifier.cpp @@ -0,0 +1,164 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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/net/tls/defaultCertificateVerifier.hpp" + +#include "vmime/net/tls/X509Certificate.hpp" + +#include "vmime/exception.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +defaultCertificateVerifier::defaultCertificateVerifier() +{ +} + + +defaultCertificateVerifier::~defaultCertificateVerifier() +{ +} + + +defaultCertificateVerifier::defaultCertificateVerifier(const defaultCertificateVerifier&) + : certificateVerifier() +{ + // Not used +} + + +void defaultCertificateVerifier::verify(ref chain) +{ + if (chain->getCount() == 0) + return; + + const string type = chain->getAt(0)->getType(); + + if (type == "X.509") + verifyX509(chain); + else + throw exceptions::unsupported_certificate_type(type); +} + + +void defaultCertificateVerifier::verifyX509(ref chain) +{ + // For every certificate in the chain, verify that the certificate + // has been issued by the next certificate in the chain + if (chain->getCount() >= 2) + { + for (unsigned int i = 0 ; i < chain->getCount() - 1 ; ++i) + { + ref cert = + chain->getAt(i).dynamicCast (); + + ref next = + chain->getAt(i + 1).dynamicCast (); + + if (!cert->checkIssuer(next)) + { + throw exceptions::certificate_verification_exception + ("Subject/issuer verification failed."); + } + } + } + + // For every certificate in the chain, verify that the certificate + // is valid at the current time + const datetime now = datetime::now(); + + for (unsigned int i = 0 ; i < chain->getCount() ; ++i) + { + ref cert = + chain->getAt(i).dynamicCast (); + + const datetime begin = cert->getActivationDate(); + const datetime end = cert->getExpirationDate(); + + if (now < begin || now > end) + { + throw exceptions::certificate_verification_exception + ("Validity date check failed."); + } + } + + // Check whether the certificate can be trusted + + // -- First, verify that the the last certificate in the chain was + // -- issued by a third-party that we trust + ref lastCert = + chain->getAt(chain->getCount() - 1).dynamicCast (); + + bool trusted = false; + + for (unsigned int i = 0 ; !trusted && i < m_x509RootCAs.size() ; ++i) + { + ref rootCa = m_x509RootCAs[i]; + + if (lastCert->verify(rootCa)) + trusted = true; + } + + // -- Next, if the issuer certificate cannot be verified against + // -- root CAs, compare the subject's certificate against the + // -- trusted certificates + ref firstCert = + chain->getAt(0).dynamicCast (); + + for (unsigned int i = 0 ; !trusted && i < m_x509TrustedCerts.size() ; ++i) + { + ref cert = m_x509TrustedCerts[i]; + + if (firstCert->equals(cert)) + trusted = true; + } + + if (!trusted) + { + throw exceptions::certificate_verification_exception + ("Cannot verify certificate against trusted certificates."); + } +} + + +void defaultCertificateVerifier::setX509RootCAs + (const std::vector >& caCerts) +{ + m_x509RootCAs = caCerts; +} + + +void defaultCertificateVerifier::setX509TrustedCerts + (const std::vector >& trustedCerts) +{ + m_x509TrustedCerts = trustedCerts; +} + + +} // tls +} // net +} // vmime + diff --git a/src/utility/stream.cpp b/src/utility/stream.cpp index 2941e5f2..04fa3308 100644 --- a/src/utility/stream.cpp +++ b/src/utility/stream.cpp @@ -117,6 +117,22 @@ void outputStreamStringAdapter::write(const value_type* const data, const size_t +// outputStreamByteArrayAdapter + +outputStreamByteArrayAdapter::outputStreamByteArrayAdapter(byteArray& array) + : m_array(array) +{ + m_array.clear(); +} + + +void outputStreamByteArrayAdapter::write(const value_type* const data, const size_type count) +{ + m_array.insert(m_array.end(), data, data + count); +} + + + // inputStreamAdapter inputStreamAdapter::inputStreamAdapter(std::istream& is) diff --git a/vmime.doxygen b/vmime.doxygen index 231ea27d..50960e13 100644 --- a/vmime.doxygen +++ b/vmime.doxygen @@ -842,7 +842,7 @@ INCLUDE_FILE_PATTERNS = # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. -PREDEFINED = VMIME_BUILDING_DOC +PREDEFINED = VMIME_BUILDING_DOC VMIME_HAVE_SASL_SUPPORT VMIME_HAVE_TLS_SUPPORT # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/vmime/exception.hpp b/vmime/exception.hpp index ed104fba..d686ab1f 100644 --- a/vmime/exception.hpp +++ b/vmime/exception.hpp @@ -77,11 +77,15 @@ public: */ virtual const char* name() const throw(); + /** Clone this object. + * + * @return a new copy of this object + */ + virtual exception* clone() const; + protected: static const exception NO_EXCEPTION; - - virtual exception* clone() const; }; @@ -818,7 +822,7 @@ public: #if VMIME_HAVE_SASL_SUPPORT -/** Base class for exceptions throw by SASL module. +/** Base class for exceptions thrown by SASL module. */ class sasl_exception : public vmime::exception @@ -866,6 +870,63 @@ public: #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + + +/** Base class for exceptions thrown by TLS module. + */ + +class tls_exception : public vmime::exception +{ +public: + + tls_exception(const string& what, const exception& other = NO_EXCEPTION); + ~tls_exception() throw(); + + exception* clone() const; + const char* name() const throw(); +}; + + +class certificate_exception : public tls_exception +{ +public: + + certificate_exception(const string& what, const exception& other = NO_EXCEPTION); + ~certificate_exception() throw(); + + exception* clone() const; + const char* name() const throw(); +}; + + +class certificate_verification_exception : public certificate_exception +{ +public: + + certificate_verification_exception(const string& what, const exception& other = NO_EXCEPTION); + ~certificate_verification_exception() throw (); + + exception* clone() const; + const char* name() const throw (); +}; + + +class unsupported_certificate_type : public certificate_exception +{ +public: + + unsupported_certificate_type(const string& type, const exception& other = NO_EXCEPTION); + ~unsupported_certificate_type() throw (); + + exception* clone() const; + const char* name() const throw (); +}; + + +#endif // VMIME_HAVE_TLS_SUPPORT + + } // exceptions diff --git a/vmime/net/imap/IMAPConnection.hpp b/vmime/net/imap/IMAPConnection.hpp index f51e97e2..f01236f2 100644 --- a/vmime/net/imap/IMAPConnection.hpp +++ b/vmime/net/imap/IMAPConnection.hpp @@ -99,6 +99,10 @@ private: void authenticateSASL(); #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + void startTLS(); +#endif // VMIME_HAVE_TLS_SUPPORT + weak_ref m_store; diff --git a/vmime/net/imap/IMAPParser.hpp b/vmime/net/imap/IMAPParser.hpp index d88fcf6e..5b267ac8 100644 --- a/vmime/net/imap/IMAPParser.hpp +++ b/vmime/net/imap/IMAPParser.hpp @@ -94,6 +94,11 @@ public: return (m_tag); } + void setSocket(weak_ref sok) + { + m_socket = sok; + } + const string lastLine() const { diff --git a/vmime/net/imap/IMAPSStore.hpp b/vmime/net/imap/IMAPSStore.hpp new file mode 100644 index 00000000..a99aff6e --- /dev/null +++ b/vmime/net/imap/IMAPSStore.hpp @@ -0,0 +1,63 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_IMAP_IMAPSSTORE_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPSSTORE_HPP_INCLUDED + + +#include "vmime/net/imap/IMAPStore.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +/** IMAPS store service. + */ + +class IMAPSStore : public IMAPStore +{ +public: + + IMAPSStore(ref sess, ref auth); + ~IMAPSStore(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + +private: + + static IMAPServiceInfos sm_infos; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_NET_IMAP_IMAPSSTORE_HPP_INCLUDED + diff --git a/vmime/net/imap/IMAPServiceInfos.hpp b/vmime/net/imap/IMAPServiceInfos.hpp new file mode 100644 index 00000000..67ef0980 --- /dev/null +++ b/vmime/net/imap/IMAPServiceInfos.hpp @@ -0,0 +1,87 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_IMAP_IMAPSERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_IMAP_IMAPSERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace imap { + + +/** Information about IMAP service. + */ + +class IMAPServiceInfos : public serviceInfos +{ +public: + + IMAPServiceInfos(const bool imaps); + + struct props + { + // IMAP-specific options +#if VMIME_HAVE_SASL_SUPPORT + serviceInfos::property PROPERTY_OPTIONS_SASL; + serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK; +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + serviceInfos::property PROPERTY_AUTH_USERNAME; + serviceInfos::property PROPERTY_AUTH_PASSWORD; + +#if VMIME_HAVE_TLS_SUPPORT + serviceInfos::property PROPERTY_CONNECTION_TLS; + serviceInfos::property PROPERTY_CONNECTION_TLS_REQUIRED; +#endif // VMIME_HAVE_TLS_SUPPORT + + serviceInfos::property PROPERTY_SERVER_ADDRESS; + serviceInfos::property PROPERTY_SERVER_PORT; + serviceInfos::property PROPERTY_SERVER_SOCKETFACTORY; + + serviceInfos::property PROPERTY_TIMEOUT_FACTORY; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector getAvailableProperties() const; + +private: + + const bool m_imaps; +}; + + +} // imap +} // net +} // vmime + + +#endif // VMIME_NET_IMAP_IMAPSERVICEINFOS_HPP_INCLUDED + diff --git a/vmime/net/imap/IMAPStore.hpp b/vmime/net/imap/IMAPStore.hpp index 0dd748cc..dcd2099b 100644 --- a/vmime/net/imap/IMAPStore.hpp +++ b/vmime/net/imap/IMAPStore.hpp @@ -31,7 +31,7 @@ #include "vmime/net/socket.hpp" #include "vmime/net/folder.hpp" -#include +#include "vmime/net/imap/IMAPServiceInfos.hpp" namespace vmime { @@ -56,7 +56,7 @@ class IMAPStore : public store public: - IMAPStore(ref sess, ref auth); + IMAPStore(ref sess, ref auth, const bool secured = false); ~IMAPStore(); const string getProtocolName() const; @@ -78,7 +78,9 @@ public: const int getCapabilities() const; -private: + const bool isSecuredConnection() const; + +protected: // Connection ref m_connection; @@ -93,39 +95,10 @@ private: std::list m_folders; + bool m_secured; // Use IMAPS - // Service infos - class _infos : public serviceInfos - { - public: - - struct props - { - // IMAP-specific options -#if VMIME_HAVE_SASL_SUPPORT - serviceInfos::property PROPERTY_OPTIONS_SASL; - serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK; -#endif // VMIME_HAVE_SASL_SUPPORT - - // Common properties - serviceInfos::property PROPERTY_AUTH_USERNAME; - serviceInfos::property PROPERTY_AUTH_PASSWORD; - - serviceInfos::property PROPERTY_SERVER_ADDRESS; - serviceInfos::property PROPERTY_SERVER_PORT; - serviceInfos::property PROPERTY_SERVER_SOCKETFACTORY; - - serviceInfos::property PROPERTY_TIMEOUT_FACTORY; - }; - - const props& getProperties() const; - - const string getPropertyPrefix() const; - const std::vector getAvailableProperties() const; - }; - - static _infos sm_infos; + static IMAPServiceInfos sm_infos; }; diff --git a/vmime/net/maildir/maildirServiceInfos.hpp b/vmime/net/maildir/maildirServiceInfos.hpp new file mode 100644 index 00000000..29ef32b9 --- /dev/null +++ b/vmime/net/maildir/maildirServiceInfos.hpp @@ -0,0 +1,64 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_MAILDIR_MAILDIRSERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_MAILDIR_MAILDIRSERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace maildir { + + +/** Information about maildir service. + */ + +class maildirServiceInfos : public serviceInfos +{ +public: + + maildirServiceInfos(); + + struct props + { + serviceInfos::property PROPERTY_SERVER_ROOTPATH; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector getAvailableProperties() const; +}; + + +} // maildir +} // net +} // vmime + + +#endif // VMIME_NET_MAILDIR_MAILDIRSERVICEINFOS_HPP_INCLUDED + diff --git a/vmime/net/maildir/maildirStore.hpp b/vmime/net/maildir/maildirStore.hpp index 41807336..f2792403 100644 --- a/vmime/net/maildir/maildirStore.hpp +++ b/vmime/net/maildir/maildirStore.hpp @@ -31,6 +31,8 @@ #include "vmime/net/socket.hpp" #include "vmime/net/folder.hpp" +#include "vmime/net/maildir/maildirServiceInfos.hpp" + #include "vmime/utility/file.hpp" #include @@ -91,22 +93,7 @@ private: // Service infos - class _infos : public serviceInfos - { - public: - - struct props - { - serviceInfos::property PROPERTY_SERVER_ROOTPATH; - }; - - const props& getProperties() const; - - const string getPropertyPrefix() const; - const std::vector getAvailableProperties() const; - }; - - static _infos sm_infos; + static maildirServiceInfos sm_infos; }; diff --git a/vmime/net/pop3/POP3SStore.hpp b/vmime/net/pop3/POP3SStore.hpp new file mode 100644 index 00000000..b1bf08ad --- /dev/null +++ b/vmime/net/pop3/POP3SStore.hpp @@ -0,0 +1,63 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_POP3_POP3SSTORE_HPP_INCLUDED +#define VMIME_NET_POP3_POP3SSTORE_HPP_INCLUDED + + +#include "vmime/net/pop3/POP3Store.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +/** POP3S store service. + */ + +class POP3SStore : public POP3Store +{ +public: + + POP3SStore(ref sess, ref auth); + ~POP3SStore(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + +private: + + static POP3ServiceInfos sm_infos; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_NET_POP3_POP3SSTORE_HPP_INCLUDED + diff --git a/vmime/net/pop3/POP3ServiceInfos.hpp b/vmime/net/pop3/POP3ServiceInfos.hpp new file mode 100644 index 00000000..04f14b78 --- /dev/null +++ b/vmime/net/pop3/POP3ServiceInfos.hpp @@ -0,0 +1,89 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_POP3_POP3SERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_POP3_POP3SERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace pop3 { + + +/** Information about POP3 service. + */ + +class POP3ServiceInfos : public serviceInfos +{ +public: + + POP3ServiceInfos(const bool pop3s); + + struct props + { + // POP3-specific options + serviceInfos::property PROPERTY_OPTIONS_APOP; + serviceInfos::property PROPERTY_OPTIONS_APOP_FALLBACK; +#if VMIME_HAVE_SASL_SUPPORT + serviceInfos::property PROPERTY_OPTIONS_SASL; + serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK; +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + serviceInfos::property PROPERTY_AUTH_USERNAME; + serviceInfos::property PROPERTY_AUTH_PASSWORD; + +#if VMIME_HAVE_TLS_SUPPORT + serviceInfos::property PROPERTY_CONNECTION_TLS; + serviceInfos::property PROPERTY_CONNECTION_TLS_REQUIRED; +#endif // VMIME_HAVE_TLS_SUPPORT + + serviceInfos::property PROPERTY_SERVER_ADDRESS; + serviceInfos::property PROPERTY_SERVER_PORT; + serviceInfos::property PROPERTY_SERVER_SOCKETFACTORY; + + serviceInfos::property PROPERTY_TIMEOUT_FACTORY; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector getAvailableProperties() const; + +private: + + const bool m_pop3s; +}; + + +} // pop3 +} // net +} // vmime + + +#endif // VMIME_NET_POP3_POP3SERVICEINFOS_HPP_INCLUDED + diff --git a/vmime/net/pop3/POP3Store.hpp b/vmime/net/pop3/POP3Store.hpp index 461741f8..b19c9790 100644 --- a/vmime/net/pop3/POP3Store.hpp +++ b/vmime/net/pop3/POP3Store.hpp @@ -31,6 +31,8 @@ #include "vmime/net/socket.hpp" #include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/pop3/POP3ServiceInfos.hpp" + #include "vmime/utility/stream.hpp" @@ -52,7 +54,7 @@ class POP3Store : public store public: - POP3Store(ref sess, ref auth); + POP3Store(ref sess, ref auth, const bool secured = false); ~POP3Store(); const string getProtocolName() const; @@ -88,6 +90,10 @@ private: void authenticateSASL(); #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + void startTLS(); +#endif // VMIME_HAVE_TLS_SUPPORT + const std::vector getCapabilities(); static const bool isSuccessResponse(const string& buffer); @@ -116,40 +122,11 @@ private: ref m_timeoutHandler; + bool m_secured; + // Service infos - class _infos : public serviceInfos - { - public: - - struct props - { - // POP3-specific options - serviceInfos::property PROPERTY_OPTIONS_APOP; - serviceInfos::property PROPERTY_OPTIONS_APOP_FALLBACK; -#if VMIME_HAVE_SASL_SUPPORT - serviceInfos::property PROPERTY_OPTIONS_SASL; - serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK; -#endif // VMIME_HAVE_SASL_SUPPORT - - // Common properties - serviceInfos::property PROPERTY_AUTH_USERNAME; - serviceInfos::property PROPERTY_AUTH_PASSWORD; - - serviceInfos::property PROPERTY_SERVER_ADDRESS; - serviceInfos::property PROPERTY_SERVER_PORT; - serviceInfos::property PROPERTY_SERVER_SOCKETFACTORY; - - serviceInfos::property PROPERTY_TIMEOUT_FACTORY; - }; - - const props& getProperties() const; - - const string getPropertyPrefix() const; - const std::vector getAvailableProperties() const; - }; - - static _infos sm_infos; + static POP3ServiceInfos sm_infos; }; diff --git a/vmime/net/sendmail/sendmailServiceInfos.hpp b/vmime/net/sendmail/sendmailServiceInfos.hpp new file mode 100644 index 00000000..7da4b776 --- /dev/null +++ b/vmime/net/sendmail/sendmailServiceInfos.hpp @@ -0,0 +1,64 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_SENDMAIL_SENDMAILSERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_SENDMAIL_SENDMAILSERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace sendmail { + + +/** Information about sendmail service. + */ + +class sendmailServiceInfos : public serviceInfos +{ +public: + + sendmailServiceInfos(); + + struct props + { + serviceInfos::property PROPERTY_BINPATH; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector getAvailableProperties() const; +}; + + +} // sendmail +} // net +} // vmime + + +#endif // VMIME_NET_SENDMAIL_SENDMAILSERVICEINFOS_HPP_INCLUDED + diff --git a/vmime/net/sendmail/sendmailTransport.hpp b/vmime/net/sendmail/sendmailTransport.hpp index 937aaf1e..39f02b8c 100644 --- a/vmime/net/sendmail/sendmailTransport.hpp +++ b/vmime/net/sendmail/sendmailTransport.hpp @@ -31,6 +31,8 @@ #include "vmime/net/socket.hpp" #include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/sendmail/sendmailServiceInfos.hpp" + #if VMIME_BUILTIN_PLATFORM_POSIX @@ -77,22 +79,7 @@ private: // Service infos - class _infos : public serviceInfos - { - public: - - struct props - { - serviceInfos::property PROPERTY_BINPATH; - }; - - const props& getProperties() const; - - const string getPropertyPrefix() const; - const std::vector getAvailableProperties() const; - }; - - static _infos sm_infos; + static sendmailServiceInfos sm_infos; }; diff --git a/vmime/net/service.hpp b/vmime/net/service.hpp index c9cc77e4..8025ebd9 100644 --- a/vmime/net/service.hpp +++ b/vmime/net/service.hpp @@ -25,6 +25,7 @@ #define VMIME_NET_SERVICE_HPP_INCLUDED +#include "vmime/config.hpp" #include "vmime/types.hpp" #include "vmime/net/session.hpp" @@ -32,6 +33,10 @@ #include "vmime/net/serviceFactory.hpp" #include "vmime/net/serviceInfos.hpp" +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/tls/certificateVerifier.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + #include "vmime/utility/progressionListener.hpp" @@ -52,7 +57,7 @@ public: virtual ~service(); - // Possible service types + /** Possible service types. */ enum Type { TYPE_STORE = 0, /**< The service is a message store. */ @@ -127,6 +132,20 @@ public: */ void setAuthenticator(ref auth); +#if VMIME_HAVE_TLS_SUPPORT + + /** Set the object responsible for verifying certificates when + * using secured connections (TLS/SSL). + */ + void setCertificateVerifier(ref cv); + + /** Get the object responsible for verifying certificates when + * using secured connections (TLS/SSL). + */ + ref getCertificateVerifier(); + +#endif // VMIME_HAVE_TLS_SUPPORT + /** Set a property for this service (service prefix is added automatically). * * WARNING: this sets the property on the session object, so all service @@ -148,10 +167,10 @@ public: { public: - initializer(const string& protocol) + initializer(const string& protocol, const Type type) { serviceFactory::getInstance()-> - template registerServiceByProtocol (protocol); + template registerServiceByProtocol (protocol, type); } }; #endif // VMIME_BUILDING_DOC @@ -160,6 +179,11 @@ private: ref m_session; ref m_auth; + +#if VMIME_HAVE_TLS_SUPPORT + ref m_certVerifier; +#endif // VMIME_HAVE_TLS_SUPPORT + }; diff --git a/vmime/net/serviceFactory.hpp b/vmime/net/serviceFactory.hpp index 71c5cb34..9401e310 100644 --- a/vmime/net/serviceFactory.hpp +++ b/vmime/net/serviceFactory.hpp @@ -45,8 +45,8 @@ namespace vmime { namespace net { -class service; class session; +class service; /** A factory to create 'service' objects for a specified protocol. @@ -78,6 +78,7 @@ public: (ref sess, ref auth) const = 0; + virtual const int getType() const = 0; virtual const string& getName() const = 0; virtual const serviceInfos& getInfos() const = 0; }; @@ -92,8 +93,8 @@ private: protected: - registeredServiceImpl(const string& name) - : m_name(name), m_servInfos(S::getInfosInstance()) + registeredServiceImpl(const string& name, const int type) + : m_type(type), m_name(name), m_servInfos(S::getInfosInstance()) { } @@ -116,8 +117,14 @@ private: return (m_name); } + const int getType() const + { + return (m_type); + } + private: + const int m_type; const string m_name; const serviceInfos& m_servInfos; }; @@ -129,12 +136,13 @@ public: /** Register a new service by its protocol name. * * @param protocol protocol name + * @param type service type */ template - void registerServiceByProtocol(const string& protocol) + void registerServiceByProtocol(const string& protocol, const int type) { const string name = utility::stringUtils::toLower(protocol); - m_services.push_back(vmime::create >(name)); + m_services.push_back(vmime::create >(name, type)); } /** Create a new service instance from a protocol name. diff --git a/vmime/net/serviceInfos.hpp b/vmime/net/serviceInfos.hpp index 03fe0a0e..14657b2b 100644 --- a/vmime/net/serviceInfos.hpp +++ b/vmime/net/serviceInfos.hpp @@ -95,6 +95,23 @@ public: * no time-out handler is used. */ static const property TIMEOUT_FACTORY; +#if VMIME_HAVE_TLS_SUPPORT + + /** The common property 'connection.tls': this is used to + * start a secured connection if it is supported by the + * server (STARTTLS extension). + */ + static const property CONNECTION_TLS; + + /** The common property 'connection.tls.required' should be + * set to 'true' to make the connection process fail if the + * server can't start a secured connection (no effect if + * 'connection.tls' is not set to 'true'). + */ + static const property CONNECTION_TLS_REQUIRED; + +#endif // VMIME_HAVE_TLS_SUPPORT + /** Value types. */ diff --git a/vmime/net/smtp/SMTPSTransport.hpp b/vmime/net/smtp/SMTPSTransport.hpp new file mode 100644 index 00000000..87eba159 --- /dev/null +++ b/vmime/net/smtp/SMTPSTransport.hpp @@ -0,0 +1,63 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_SMTP_SMTPSSTORE_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPSSTORE_HPP_INCLUDED + + +#include "vmime/net/smtp/SMTPTransport.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +/** SMTPS transport service. + */ + +class SMTPSTransport : public SMTPTransport +{ +public: + + SMTPSTransport(ref sess, ref auth); + ~SMTPSTransport(); + + const string getProtocolName() const; + + static const serviceInfos& getInfosInstance(); + const serviceInfos& getInfos() const; + +private: + + static SMTPServiceInfos sm_infos; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_NET_SMTP_SMTPSSTORE_HPP_INCLUDED + diff --git a/vmime/net/smtp/SMTPServiceInfos.hpp b/vmime/net/smtp/SMTPServiceInfos.hpp new file mode 100644 index 00000000..ac3ae372 --- /dev/null +++ b/vmime/net/smtp/SMTPServiceInfos.hpp @@ -0,0 +1,88 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_SMTP_SMTPSERVICEINFOS_HPP_INCLUDED +#define VMIME_NET_SMTP_SMTPSERVICEINFOS_HPP_INCLUDED + + +#include "vmime/config.hpp" +#include "vmime/net/serviceInfos.hpp" + + +namespace vmime { +namespace net { +namespace smtp { + + +/** Information about SMTP service. + */ + +class SMTPServiceInfos : public serviceInfos +{ +public: + + SMTPServiceInfos(const bool smtps); + + struct props + { + // SMTP-specific options + serviceInfos::property PROPERTY_OPTIONS_NEEDAUTH; +#if VMIME_HAVE_SASL_SUPPORT + serviceInfos::property PROPERTY_OPTIONS_SASL; + serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK; +#endif // VMIME_HAVE_SASL_SUPPORT + + // Common properties + serviceInfos::property PROPERTY_AUTH_USERNAME; + serviceInfos::property PROPERTY_AUTH_PASSWORD; + +#if VMIME_HAVE_TLS_SUPPORT + serviceInfos::property PROPERTY_CONNECTION_TLS; + serviceInfos::property PROPERTY_CONNECTION_TLS_REQUIRED; +#endif // VMIME_HAVE_TLS_SUPPORT + + serviceInfos::property PROPERTY_SERVER_ADDRESS; + serviceInfos::property PROPERTY_SERVER_PORT; + serviceInfos::property PROPERTY_SERVER_SOCKETFACTORY; + + serviceInfos::property PROPERTY_TIMEOUT_FACTORY; + }; + + const props& getProperties() const; + + const string getPropertyPrefix() const; + const std::vector getAvailableProperties() const; + +private: + + const bool m_smtps; +}; + + +} // smtp +} // net +} // vmime + + +#endif // VMIME_NET_SMTP_SMTPSERVICEINFOS_HPP_INCLUDED + diff --git a/vmime/net/smtp/SMTPTransport.hpp b/vmime/net/smtp/SMTPTransport.hpp index ae22af7f..65d8537b 100644 --- a/vmime/net/smtp/SMTPTransport.hpp +++ b/vmime/net/smtp/SMTPTransport.hpp @@ -31,6 +31,8 @@ #include "vmime/net/socket.hpp" #include "vmime/net/timeoutHandler.hpp" +#include "vmime/net/smtp/SMTPServiceInfos.hpp" + namespace vmime { namespace net { @@ -44,7 +46,7 @@ class SMTPTransport : public transport { public: - SMTPTransport(ref sess, ref auth); + SMTPTransport(ref sess, ref auth, const bool secured = false); ~SMTPTransport(); const string getProtocolName() const; @@ -77,6 +79,9 @@ private: void authenticateSASL(); #endif // VMIME_HAVE_SASL_SUPPORT +#if VMIME_HAVE_TLS_SUPPORT + void startTLS(); +#endif // VMIME_HAVE_TLS_SUPPORT ref m_socket; bool m_authentified; @@ -89,39 +94,11 @@ private: ref m_timeoutHandler; + bool m_secured; + // Service infos - class _infos : public serviceInfos - { - public: - - struct props - { - // SMTP-specific options - serviceInfos::property PROPERTY_OPTIONS_NEEDAUTH; -#if VMIME_HAVE_SASL_SUPPORT - serviceInfos::property PROPERTY_OPTIONS_SASL; - serviceInfos::property PROPERTY_OPTIONS_SASL_FALLBACK; -#endif // VMIME_HAVE_SASL_SUPPORT - - // Common properties - serviceInfos::property PROPERTY_AUTH_USERNAME; - serviceInfos::property PROPERTY_AUTH_PASSWORD; - - serviceInfos::property PROPERTY_SERVER_ADDRESS; - serviceInfos::property PROPERTY_SERVER_PORT; - serviceInfos::property PROPERTY_SERVER_SOCKETFACTORY; - - serviceInfos::property PROPERTY_TIMEOUT_FACTORY; - }; - - const props& getProperties() const; - - const string getPropertyPrefix() const; - const std::vector getAvailableProperties() const; - }; - - static _infos sm_infos; + static SMTPServiceInfos sm_infos; }; diff --git a/vmime/net/tls/TLSSession.hpp b/vmime/net/tls/TLSSession.hpp new file mode 100644 index 00000000..e946c102 --- /dev/null +++ b/vmime/net/tls/TLSSession.hpp @@ -0,0 +1,95 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSESSION_HPP_INCLUDED + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/TLSSocket.hpp" + +#include "vmime/net/tls/certificateVerifier.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +/** Describe a TLS connection between a client and a server. + */ +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); + + /** Create a new socket that adds a TLS security layer around + * an existing socket. You should create only one socket + * per session. + * + * @param sok socket to wrap + * @return TLS socket wrapper + */ + ref getSocket(ref sok); + + /** Get the object responsible for verifying certificates when + * using secured connections (TLS/SSL). + */ + ref getCertificateVerifier(); + +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; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_NET_TLS_TLSSESSION_HPP_INCLUDED + diff --git a/vmime/net/tls/TLSSocket.hpp b/vmime/net/tls/TLSSocket.hpp new file mode 100644 index 00000000..075a77ef --- /dev/null +++ b/vmime/net/tls/TLSSocket.hpp @@ -0,0 +1,125 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_HPP_INCLUDED +#define VMIME_NET_TLS_TLSSOCKET_HPP_INCLUDED + + +#include "vmime/exception.hpp" + +#include "vmime/net/socket.hpp" +#include "vmime/net/timeoutHandler.hpp" + +#include "vmime/net/tls/certificateChain.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class TLSSession; + + +/** Add a TLS security layer to an existing socket. + */ +class TLSSocket : public socket +{ + friend class vmime::creator; + +protected: + + /** Create a new socket object that adds a security layer + * around an existing socket. + * + * @param session TLS session + * @param sok socket to wrap + */ + TLSSocket(ref session, ref sok); + +public: + + ~TLSSocket(); + + + /** Starts a TLS handshake on this connection. + * + * @throw exceptions::tls_exception if a fatal error occurs + * during the negociation process, exceptions::operation_timed_out + * if a time-out occurs + */ + void handshake(ref toHandler = NULL); + + /** 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(); + + + // Implementation of 'socket' + void connect(const string& address, const port_t port); + void disconnect(); + const bool isConnected() const; + + void receive(string& buffer); + const int receiveRaw(char* buffer, const int count); + + void send(const string& buffer); + void sendRaw(const char* buffer, const int count); + +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 ssize_t gnutlsPushFunc(void* trspt, const void* data, size_t len); + static ssize_t 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_NET_TLS_TLSSOCKET_HPP_INCLUDED + diff --git a/vmime/net/tls/X509Certificate.hpp b/vmime/net/tls/X509Certificate.hpp new file mode 100644 index 00000000..5edd4e46 --- /dev/null +++ b/vmime/net/tls/X509Certificate.hpp @@ -0,0 +1,158 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_X509CERTIFICATE_HPP_INCLUDED +#define VMIME_NET_TLS_X509CERTIFICATE_HPP_INCLUDED + + +#include "vmime/net/tls/certificate.hpp" + +#include "vmime/utility/stream.hpp" + +#include "vmime/base.hpp" +#include "vmime/dateTime.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +/** Identity certificate based on X.509 standard. + */ +class X509Certificate : public certificate +{ + friend class vmime::creator; + +protected: + + X509Certificate(); + X509Certificate(const X509Certificate&); + +public: + + ~X509Certificate(); + + /** Supported encodings for X.509 certificates. */ + enum Format + { + FORMAT_DER, /**< DER encoding */ + FORMAT_PEM /**< PEM encoding */ + }; + + /** Supported digest algorithms (used for fingerprint). */ + enum DigestAlgorithm + { + DIGEST_MD5, /**< MD5 digest */ + DIGEST_SHA1 /**< SHA1 digest */ + }; + + + /** Imports a DER or PEM encoded X.509 certificate. + * + * @param is input stream to read data from + * @return a X.509 certificate, or NULL if the given data does not + * represent a valid certificate + */ + static ref import(utility::inputStream& is); + + /** Imports a DER or PEM encoded X.509 certificate. + * + * @param data points to raw data + * @param length size of data + * @return a X.509 certificate, or NULL if the given data does not + * represent a valid certificate + */ + static ref import(const byte* data, const unsigned int length); + + /** Exports this X.509 certificate to the specified format. + * + * @param os output stream into which write data + * @param format output format + */ + void write(utility::outputStream& os, const Format format) const; + + /** Returns the X.509 certificate's serial number. This is obtained + * by the X.509 Certificate 'serialNumber' field. Serial is not + * always a 32 or 64bit number. Some CAs use large serial numbers, + * thus it may be wise to handle it as something opaque. + * + * @return serial number of this certificate + */ + const byteArray getSerialNumber() const; + + /** Checks if this certificate has the given issuer. + * + * @param issuer certificate of a possible issuer + * @return true if this certificate was issued by the given issuer, + * false otherwise + */ + const bool checkIssuer(ref issuer) const; + + /** 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 + */ + const bool verify(ref caCert) const; + + /** 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; + + /** 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; + + /** 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; + const int getVersion() const; + const bool equals(ref other) const; + +private: + + class X509CertificateInternalData* m_data; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_NET_TLS_X509CERTIFICATE_HPP_INCLUDED + diff --git a/vmime/net/tls/certificate.hpp b/vmime/net/tls/certificate.hpp new file mode 100644 index 00000000..c070484b --- /dev/null +++ b/vmime/net/tls/certificate.hpp @@ -0,0 +1,77 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_CERTIFICATE_HPP_INCLUDED +#define VMIME_NET_TLS_CERTIFICATE_HPP_INCLUDED + + +#include "vmime/types.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +/** Identity certificate for a peer. + */ +class certificate : public object +{ +public: + + /** Returns the encoded form of this certificate (for example, + * X.509 certificates are encoded as ASN.1 DER). + * + * @return the encoded form of this certificate + */ + virtual const byteArray getEncoded() const = 0; + + /** Return the type of this certificate. + * + * @return the type of this certificate + */ + virtual const string getType() const = 0; + + /** Return the version of this certificate. + * + * @return the version of this certificate + */ + virtual const int getVersion() const = 0; + + /** Checks if two certificates are the same. + * + * @param other certificate to compare with + * @return true if the two certificates are the same, + * false otherwise + */ + virtual const bool equals(ref other) const = 0; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_NET_TLS_CERTIFICATE_HPP_INCLUDED + diff --git a/vmime/net/tls/certificateChain.hpp b/vmime/net/tls/certificateChain.hpp new file mode 100644 index 00000000..332e3f70 --- /dev/null +++ b/vmime/net/tls/certificateChain.hpp @@ -0,0 +1,79 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_CERTIFICATECHAIN_HPP_INCLUDED +#define VMIME_NET_TLS_CERTIFICATECHAIN_HPP_INCLUDED + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/certificate.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +/** An ordered list of certificates, from the subject certificate to + * the issuer certificate. + */ +class certificateChain : public object +{ +public: + + /** Construct a new certificateChain object given an ordered list + * of certificates. + * + * @param certs chain of certificates + */ + certificateChain(const std::vector >& certs); + + /** Return the number of certificates in the chain. + * + * @return number of certificates in the chain + */ + const unsigned int getCount() const; + + /** Return the certificate at the specified position. 0 is the + * subject certificate, 1 is the issuer's certificate, 2 is + * the issuer's issuer, etc. + * + * @param index position at which to retrieve certificate + * @return certificate at the specified position + */ + ref getAt(const unsigned int index); + +protected: + + std::vector > m_certs; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_NET_TLS_CERTIFICATECHAIN_HPP_INCLUDED + diff --git a/vmime/net/tls/certificateVerifier.hpp b/vmime/net/tls/certificateVerifier.hpp new file mode 100644 index 00000000..fd235b48 --- /dev/null +++ b/vmime/net/tls/certificateVerifier.hpp @@ -0,0 +1,60 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_CERTIFICATEVERIFIER_HPP_INCLUDED +#define VMIME_NET_TLS_CERTIFICATEVERIFIER_HPP_INCLUDED + + +#include "vmime/types.hpp" + +#include "vmime/net/tls/certificateChain.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +/** Verify that a certificate path issued by a server can be trusted. + */ +class certificateVerifier : public object +{ +public: + + /** Verify that the specified certificate chain is trusted. + * + * @param chain certificate chain + * @throw exceptions::certificate_verification_exception if one + * or more certificates can not be trusted + */ + virtual void verify(ref chain) = 0; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_NET_TLS_CERTIFICATEVERIFIER_HPP_INCLUDED + diff --git a/vmime/net/tls/defaultCertificateVerifier.hpp b/vmime/net/tls/defaultCertificateVerifier.hpp new file mode 100644 index 00000000..3713fd21 --- /dev/null +++ b/vmime/net/tls/defaultCertificateVerifier.hpp @@ -0,0 +1,88 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 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 2 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_DEFAULTCERTIFICATEVERIFIER_HPP_INCLUDED +#define VMIME_NET_TLS_DEFAULTCERTIFICATEVERIFIER_HPP_INCLUDED + + +#include "vmime/net/tls/certificateVerifier.hpp" + + +namespace vmime { +namespace net { +namespace tls { + + +class X509Certificate; + + +/** Default implementation for certificate verification. + */ +class defaultCertificateVerifier : public certificateVerifier +{ +private: + + defaultCertificateVerifier(const defaultCertificateVerifier&); + +public: + + defaultCertificateVerifier(); + ~defaultCertificateVerifier(); + + /** Sets a list of X.509 certificates that are trusted. + * + * @param trustedCerts list of trusted certificates + */ + void setX509TrustedCerts(const std::vector >& trustedCerts); + + /** Sets the X.509 root CAs used for certificate verification. + * + * @param caCerts list of root CAs + */ + void setX509RootCAs(const std::vector >& caCerts); + + + // Implementation of 'certificateVerifier' + void verify(ref chain); + +private: + + /** Verify a chain of X.509 certificates. + * + * @param chain list of X.509 certificates + */ + void verifyX509(ref chain); + + + std::vector > m_x509RootCAs; + std::vector > m_x509TrustedCerts; +}; + + +} // tls +} // net +} // vmime + + +#endif // VMIME_NET_TLS_DEFAULTCERTIFICATEVERIFIER_HPP_INCLUDED + diff --git a/vmime/utility/stream.hpp b/vmime/utility/stream.hpp index 4a5bd216..2ebd41c5 100644 --- a/vmime/utility/stream.hpp +++ b/vmime/utility/stream.hpp @@ -229,6 +229,23 @@ private: }; +/** An adapter class for byte array output. + */ + +class outputStreamByteArrayAdapter : public outputStream +{ +public: + + outputStreamByteArrayAdapter(byteArray& array); + + void write(const value_type* const data, const size_type count); + +private: + + byteArray m_array; +}; + + /** An adapter class for C++ standard input streams. */ diff --git a/vmime/vmime.hpp b/vmime/vmime.hpp index b2245917..c7a0b4f6 100644 --- a/vmime/vmime.hpp +++ b/vmime/vmime.hpp @@ -119,5 +119,18 @@ #include "vmime/net/message.hpp" #endif // VMIME_HAVE_MESSAGING_FEATURES +// Net/TLS +#if VMIME_HAVE_TLS_SUPPORT + #include "vmime/net/tls/certificate.hpp" + #include "vmime/net/tls/certificateChain.hpp" + #include "vmime/net/tls/certificateVerifier.hpp" + + #include "vmime/net/tls/X509Certificate.hpp" + + #include "vmime/net/tls/defaultCertificateVerifier.hpp" + + #include "vmime/net/tls/TLSSession.hpp" +#endif // VMIME_HAVE_TLS_SUPPORT + #endif // VMIME_INCLUDED