diff options
-rw-r--r-- | src/vmime/exception.hpp | 1 | ||||
-rw-r--r-- | src/vmime/messageParser.cpp | 19 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPConnection.cpp | 50 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPConnection.hpp | 2 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPFolder.cpp | 21 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPMessage.cpp | 34 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPMessagePartContentHandler.cpp | 14 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPParser.hpp | 143 | ||||
-rw-r--r-- | src/vmime/net/imap/IMAPStore.cpp | 14 | ||||
-rw-r--r-- | src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp | 15 | ||||
-rw-r--r-- | src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp | 2 | ||||
-rw-r--r-- | src/vmime/net/tls/openssl/OpenSSLInitializer.cpp | 6 | ||||
-rw-r--r-- | src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp | 14 | ||||
-rw-r--r-- | src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp | 2 | ||||
-rw-r--r-- | src/vmime/security/cert/defaultCertificateVerifier.cpp | 101 | ||||
-rw-r--r-- | src/vmime/security/cert/defaultCertificateVerifier.hpp | 7 | ||||
-rw-r--r-- | src/vmime/security/sasl/builtinSASLMechanism.cpp | 4 | ||||
-rw-r--r-- | tests/net/imap/IMAPParserTest.cpp | 56 |
18 files changed, 372 insertions, 133 deletions
diff --git a/src/vmime/exception.hpp b/src/vmime/exception.hpp index da0ec246..83f6c9c0 100644 --- a/src/vmime/exception.hpp +++ b/src/vmime/exception.hpp @@ -34,6 +34,7 @@ namespace vmime { +typedef std::exception_ptr exception_ptr; /** Base class for VMime exceptions. */ diff --git a/src/vmime/messageParser.cpp b/src/vmime/messageParser.cpp index 21ac09bb..d7dd592a 100644 --- a/src/vmime/messageParser.cpp +++ b/src/vmime/messageParser.cpp @@ -82,12 +82,18 @@ void messageParser::parse(const shared_ptr <const message>& msg) { // Date shared_ptr <const headerField> recv = msg->getHeader()->findField(fields::RECEIVED); + static const datetime unsetDate; + + m_date = unsetDate; if (recv) { m_date = recv->getValue <relay>()->getDate(); - } else { + } + + // RECEIVED may in some cases contain no date at all + if (unsetDate == m_date) { shared_ptr <const headerField> date = msg->getHeader()->findField(fields::DATE); @@ -204,7 +210,16 @@ bool messageParser::findSubTextParts( } else { - // No "Content-type" field. + // No "Content-type" field. RFC2045 section 5.2 assumes this is TEXT/PLAIN + try { + shared_ptr <textPart> txtPart = textPartFactory::getInstance()->create(mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); + txtPart->parse(msg, part, p); + + m_textParts.push_back(txtPart); + } + catch (exceptions::no_factory_available& e) { + // Content-type not recognized. + } } } diff --git a/src/vmime/net/imap/IMAPConnection.cpp b/src/vmime/net/imap/IMAPConnection.cpp index df3da1c3..0e4020f1 100644 --- a/src/vmime/net/imap/IMAPConnection.cpp +++ b/src/vmime/net/imap/IMAPConnection.cpp @@ -51,14 +51,13 @@ #include <sstream> - // Helpers for service properties #define GET_PROPERTY(type, prop) \ - (m_store.lock()->getInfos().getPropertyValue <type>(getSession(), \ - dynamic_cast <const IMAPServiceInfos&>(m_store.lock()->getInfos()).getProperties().prop)) + (getStoreOrThrow()->getInfos().getPropertyValue <type>(getSession(), \ + dynamic_cast <const IMAPServiceInfos&>(getStoreOrThrow()->getInfos()).getProperties().prop)) #define HAS_PROPERTY(prop) \ - (m_store.lock()->getInfos().hasProperty(getSession(), \ - dynamic_cast <const IMAPServiceInfos&>(m_store.lock()->getInfos()).getProperties().prop)) + (getStoreOrThrow()->getInfos().hasProperty(getSession(), \ + dynamic_cast <const IMAPServiceInfos&>(getStoreOrThrow()->getInfos()).getProperties().prop)) namespace vmime { @@ -127,6 +126,10 @@ void IMAPConnection::connect() { shared_ptr <IMAPStore> store = m_store.lock(); + if (!store) { + throw exceptions::illegal_state("Store not found"); + } + // Create the time-out handler if (store->getTimeoutHandlerFactory()) { m_timeoutHandler = store->getTimeoutHandlerFactory()->create(); @@ -244,6 +247,17 @@ void IMAPConnection::connect() { setState(STATE_AUTHENTICATED); } +shared_ptr <IMAPStore> IMAPConnection::getStoreOrThrow() { + + auto store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store not found"); + } + + return store; +} + void IMAPConnection::authenticate() { @@ -529,9 +543,15 @@ void IMAPConnection::startTLS() { throw exceptions::command_error("STARTTLS", resp->getErrorLog(), "bad response"); } + auto store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store not found"); + } + shared_ptr <tls::TLSSession> tlsSession = tls::TLSSession::create( - m_store.lock()->getCertificateVerifier(), - m_store.lock()->getSession()->getTLSProperties() + store->getCertificateVerifier(), + store->getSession()->getTLSProperties() ); shared_ptr <tls::TLSSocket> tlsSocket = tlsSession->getSocket(m_socket); @@ -701,11 +721,11 @@ shared_ptr <connectionInfos> IMAPConnection::getConnectionInfos() const { void IMAPConnection::disconnect() { - if (!isConnected()) { + bool wasConnected = isConnected(); + internalDisconnect(); + if (!wasConnected) { throw exceptions::not_connected(); } - - internalDisconnect(); } @@ -714,7 +734,9 @@ void IMAPConnection::internalDisconnect() { if (isConnected()) { IMAPCommand::LOGOUT()->send(dynamicCast <IMAPConnection>(shared_from_this())); + } + if (m_socket) { m_socket->disconnect(); m_socket = null; } @@ -836,7 +858,13 @@ shared_ptr <IMAPStore> IMAPConnection::getStore() { shared_ptr <session> IMAPConnection::getSession() { - return m_store.lock()->getSession(); + auto store = m_store.lock(); + + if (!store) { + throw exceptions::illegal_state("Store not found"); + } + + return store->getSession(); } diff --git a/src/vmime/net/imap/IMAPConnection.hpp b/src/vmime/net/imap/IMAPConnection.hpp index 99750d49..d229d0f7 100644 --- a/src/vmime/net/imap/IMAPConnection.hpp +++ b/src/vmime/net/imap/IMAPConnection.hpp @@ -114,6 +114,8 @@ public: private: + shared_ptr <IMAPStore> getStoreOrThrow(); + void authenticate(); #if VMIME_HAVE_SASL_SUPPORT void authenticateSASL(); diff --git a/src/vmime/net/imap/IMAPFolder.cpp b/src/vmime/net/imap/IMAPFolder.cpp index 3a33469a..2e07ac3a 100644 --- a/src/vmime/net/imap/IMAPFolder.cpp +++ b/src/vmime/net/imap/IMAPFolder.cpp @@ -80,7 +80,11 @@ IMAPFolder::~IMAPFolder() { if (store) { if (m_open) { - close(false); + try { + close(false); + } catch (...) { + // Ignore exception here to make sure unregisterFolder is called + } } store->unregisterFolder(this); @@ -290,9 +294,17 @@ void IMAPFolder::close(const bool expunge) { shared_ptr <IMAPStore> store = m_store.lock(); if (!store) { + if (m_connection && m_connection->isConnected()) + m_connection->disconnect(); + m_connection = nullptr; + m_open = false; throw exceptions::illegal_state("Store disconnected"); } + if (!m_connection || !m_connection->isConnected()) { + throw exceptions::illegal_state("Folder not connected"); + } + if (!isOpen()) { throw exceptions::illegal_state("Folder not open"); } @@ -314,7 +326,7 @@ void IMAPFolder::close(const bool expunge) { oldConnection->disconnect(); // Now use default store connection - m_connection = m_store.lock()->connection(); + m_connection = store->connection(); m_open = false; m_mode = -1; @@ -546,7 +558,6 @@ bool IMAPFolder::isOpen() const { return m_open; } - shared_ptr <message> IMAPFolder::getMessage(const size_t num) { if (!isOpen()) { @@ -1422,6 +1433,10 @@ void IMAPFolder::noop() { throw exceptions::illegal_state("Store disconnected"); } + if (!m_connection || !m_connection->isConnected()) { + throw exceptions::illegal_state("Store disconnected"); + } + IMAPCommand::NOOP()->send(m_connection); scoped_ptr <IMAPParser::response> resp(m_connection->readResponse()); diff --git a/src/vmime/net/imap/IMAPMessage.cpp b/src/vmime/net/imap/IMAPMessage.cpp index f74a4a4b..0bf8fbe6 100644 --- a/src/vmime/net/imap/IMAPMessage.cpp +++ b/src/vmime/net/imap/IMAPMessage.cpp @@ -38,6 +38,8 @@ #include "vmime/net/imap/IMAPMessagePart.hpp" #include "vmime/net/imap/IMAPMessagePartContentHandler.hpp" +#include "vmime/messageId.hpp" +#include "vmime/messageIdSequence.hpp" #include "vmime/utility/outputStreamAdapter.hpp" #include <sstream> @@ -318,6 +320,14 @@ size_t IMAPMessage::extractImpl( shared_ptr <const IMAPFolder> folder = m_folder.lock(); + if (!folder) { + throw exceptions::folder_not_found(); + } + + if (!folder->isOpen()) { + throw exceptions::illegal_state("Folder not open"); + } + IMAPMessage_literalHandler literalHandler(os, progress); if (length == 0) { @@ -474,7 +484,6 @@ size_t IMAPMessage::extractImpl( return literalHandler.getTarget()->getBytesWritten(); } - int IMAPMessage::processFetchResponse( const fetchAttributes& options, const IMAPParser::message_data& msgData @@ -553,7 +562,7 @@ int IMAPMessage::processFetchResponse( IMAPUtils::convertAddressList(*(env->env_reply_to), replyTo); if (!replyTo.isEmpty()) { - hdr->ReplyTo()->setValue(*(replyTo.getMailboxAt(0))); + hdr->ReplyTo()->setValue(replyTo.toAddressList()); } // Cc @@ -571,6 +580,19 @@ int IMAPMessage::processFetchResponse( if (!bcc.isEmpty()) { hdr->Bcc()->setValue(bcc.toAddressList()); } + + // Message-ID + if (!env->env_message_id->isNIL) { + hdr->MessageId()->setValue(messageId(env->env_message_id->value)); + } + + // In-Reply-To + if (!env->env_in_reply_to->isNIL) { + shared_ptr <messageId> mid = make_shared <messageId>(env->env_in_reply_to->value); + messageIdSequence sequence; + sequence.appendMessageId(mid); + hdr->InReplyTo()->setValue(sequence); + } } break; @@ -717,10 +739,16 @@ shared_ptr <vmime::message> IMAPMessage::getParsedMessage() { } catch (exceptions::unfetched_object&) { + auto folder = m_folder.lock(); + + if (!folder) { + throw exceptions::folder_not_found(); + } + std::vector <shared_ptr <message> > msgs; msgs.push_back(dynamicCast <IMAPMessage>(shared_from_this())); - m_folder.lock()->fetchMessages( + folder->fetchMessages( msgs, fetchAttributes(fetchAttributes::STRUCTURE), /* progress */ NULL ); diff --git a/src/vmime/net/imap/IMAPMessagePartContentHandler.cpp b/src/vmime/net/imap/IMAPMessagePartContentHandler.cpp index 85f6cb6e..040d2ab0 100644 --- a/src/vmime/net/imap/IMAPMessagePartContentHandler.cpp +++ b/src/vmime/net/imap/IMAPMessagePartContentHandler.cpp @@ -36,7 +36,6 @@ #include "vmime/utility/outputStreamAdapter.hpp" #include "vmime/utility/inputStreamStringAdapter.hpp" - namespace vmime { namespace net { namespace imap { @@ -71,6 +70,9 @@ void IMAPMessagePartContentHandler::generate( shared_ptr <IMAPMessage> msg = m_message.lock(); shared_ptr <messagePart> part = m_part.lock(); + if (!msg || !part) + throw exceptions::message_not_found(); + // Data is already encoded if (isEncoded()) { @@ -140,6 +142,9 @@ void IMAPMessagePartContentHandler::extract( shared_ptr <IMAPMessage> msg = m_message.lock(); shared_ptr <messagePart> part = m_part.lock(); + if (!msg || !part) + throw exceptions::message_not_found(); + // No decoding to perform if (!isEncoded()) { @@ -172,12 +177,17 @@ void IMAPMessagePartContentHandler::extractRaw( shared_ptr <IMAPMessage> msg = m_message.lock(); shared_ptr <messagePart> part = m_part.lock(); + if (!msg || !part) + throw exceptions::message_not_found(); + msg->extractPart(part, os, progress); } size_t IMAPMessagePartContentHandler::getLength() const { - + shared_ptr <messagePart> part = m_part.lock(); + if (!part) + throw exceptions::message_not_found(); return m_part.lock()->getSize(); } diff --git a/src/vmime/net/imap/IMAPParser.hpp b/src/vmime/net/imap/IMAPParser.hpp index 409362da..59bab777 100644 --- a/src/vmime/net/imap/IMAPParser.hpp +++ b/src/vmime/net/imap/IMAPParser.hpp @@ -1022,48 +1022,6 @@ public: string value; }; - // - // unquoted_text_nonstrict: this is an alternate version of quoted_text, - // for use in non-strict mode. We stop at the first space character. - // - - DECLARE_COMPONENT(unquoted_text_nonstrict) - - bool parseImpl(IMAPParser& /* parser */, string& line, size_t* currentPos) { - - size_t pos = *currentPos; - size_t len = 0; - - value.reserve(line.length() - pos); - - for (bool end = false ; !end && pos < line.length() ; ) { - - const unsigned char c = line[pos]; - - if (c >= 0x01 && c <= 0x7f && // CHAR - c != 0x0a && c != 0x0d && // CR and LF - c != 0x20 && // SPACE - c != 0x09) { // TAB - - value += c; - - ++pos; - ++len; - - } else { - - end = true; - } - } - - *currentPos = pos; - - return true; - } - - - string value; - }; // // nil ::= "NIL" @@ -1212,47 +1170,6 @@ public: DEBUG_FOUND("string[literal]", "<length=" << length << ", value='" << value << "'>"); - // In non-strict mode, accept unquoted strings, but stop at next SPACE - } else if (!parser.isStrict()) { - - shared_ptr <unquoted_text_nonstrict> text; - VIMAP_PARSER_GET(unquoted_text_nonstrict, text); - - if (parser.m_literalHandler != NULL) { - - shared_ptr <literalHandler::target> target = - parser.m_literalHandler->targetFor(*m_component, m_data); - - if (target != NULL) { - - value = "[literal-handler]"; - - const size_t length = text->value.length(); - utility::progressListener* progress = target->progressListener(); - - if (progress) { - progress->start(length); - } - - target->putData(text->value); - - if (progress) { - progress->progress(length, length); - progress->stop(length); - } - - } else { - - value = text->value; - } - - } else { - - value = text->value; - } - - DEBUG_FOUND("string[non-strict]", "<length=" << value.length() << ", value='" << value << "'>"); - } else { VIMAP_PARSER_FAIL(); @@ -1298,9 +1215,21 @@ public: // - // astring ::= atom / string + // astring = 1*ASTRING-CHAR / string + // + // ASTRING-CHAR = ATOM-CHAR / resp-specials + // + // ATOM-CHAR = <any CHAR except atom-specials> + // + // atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / + // quoted-specials / resp-specials + // + // list-wildcards = "%" / "*" + // + // quoted-specials = DQUOTE / "\" + // + // resp-specials = "]" // - DECLARE_COMPONENT(astring) bool parseImpl(IMAPParser& parser, string& line, size_t* currentPos) { @@ -1311,11 +1240,43 @@ public: VIMAP_PARSER_TRY_GET(xstring, str); if (str) { + value = str->value; + } else { - std::unique_ptr <atom> at; - VIMAP_PARSER_GET(atom, at); - value = at->value; + + value.reserve(line.length() - pos); + + for (bool end = false ; !end && pos < line.length() ; ) { + + const unsigned char c = line[pos]; + + if (!parser.isStrict() || (c >= 0x01 && c <= 0x7f)) { // CHAR or any byte in non-strict mode + + if (c == '(' || + c == ')' || + c == '{' || + c == 0x20 || // SP + c == 0x0a || c == 0x0d || // CR and LF + c <= 0x1f || c == 0x7f || // CTL + c == '%' || c == '*' || // list-wildcards + c == '"' || c == '\\' || // quoted-specials + (parser.isStrict() && c == ']')) { // resp-specials + + end = true; + + } else { + + value += c; + + ++pos; + } + + } else { + + end = true; + } + } } *currentPos = pos; @@ -4912,6 +4873,9 @@ public: shared_ptr <timeoutHandler> toh = m_timeoutHandler.lock(); shared_ptr <socket> sok = m_socket.lock(); + if (!sok) + throw exceptions::illegal_state("Store disconnected"); + if (toh) { toh->resetTimeOut(); } @@ -4957,6 +4921,9 @@ public: shared_ptr <timeoutHandler> toh = m_timeoutHandler.lock(); shared_ptr <socket> sok = m_socket.lock(); + if (!sok) + throw exceptions::illegal_state("Store disconnected"); + if (m_progress) { m_progress->start(count); } diff --git a/src/vmime/net/imap/IMAPStore.cpp b/src/vmime/net/imap/IMAPStore.cpp index eafa4442..be5c4de7 100644 --- a/src/vmime/net/imap/IMAPStore.cpp +++ b/src/vmime/net/imap/IMAPStore.cpp @@ -179,9 +179,7 @@ shared_ptr <IMAPConnection> IMAPStore::getConnection() { void IMAPStore::disconnect() { - if (!isConnected()) { - throw exceptions::not_connected(); - } + bool wasConnected = isConnected(); for (std::list <IMAPFolder*>::iterator it = m_folders.begin() ; it != m_folders.end() ; ++it) { @@ -191,10 +189,14 @@ void IMAPStore::disconnect() { m_folders.clear(); + if (m_connection) { + m_connection->disconnect(); + m_connection = null; + } - m_connection->disconnect(); - - m_connection = null; + if (!wasConnected) { + throw exceptions::not_connected(); + } } diff --git a/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp index 53e4eaee..97466fc5 100644 --- a/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp +++ b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.cpp @@ -66,7 +66,7 @@ TLSSocket_GnuTLS::TLSSocket_GnuTLS( : m_session(session), m_wrapped(sok), m_connected(false), - m_ex(NULL), + m_ex(nullptr), m_status(0), m_errno(0) { @@ -407,7 +407,7 @@ ssize_t TLSSocket_GnuTLS::gnutlsPushFunc( // Workaround for non-portable behaviour when throwing C++ exceptions // from C functions (GNU TLS) - sok->m_ex = e.clone(); + sok->m_ex = std::current_exception(); return -1; } } @@ -440,7 +440,7 @@ ssize_t TLSSocket_GnuTLS::gnutlsPullFunc( // Workaround for non-portable behaviour when throwing C++ exceptions // from C functions (GNU TLS) - sok->m_ex = e.clone(); + sok->m_ex = std::current_exception(); return -1; } } @@ -525,18 +525,15 @@ shared_ptr <security::cert::certificateChain> TLSSocket_GnuTLS::getPeerCertifica void TLSSocket_GnuTLS::throwException() { - if (m_ex) { - throw *m_ex; + if (!!m_ex) { + std::rethrow_exception(m_ex); } } void TLSSocket_GnuTLS::resetException() { - if (m_ex) { - delete m_ex; - m_ex = NULL; - } + m_ex = nullptr; } diff --git a/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp index 0ac3e700..a90379fd 100644 --- a/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp +++ b/src/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp @@ -109,7 +109,7 @@ private: byte_t m_buffer[65536]; - exception* m_ex; + exception_ptr m_ex; unsigned int m_status; int m_errno; diff --git a/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp b/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp index c7b1013d..49b34b54 100644 --- a/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp +++ b/src/vmime/net/tls/openssl/OpenSSLInitializer.cpp @@ -115,6 +115,12 @@ void OpenSSLInitializer::initialize() { OPENSSL_config(NULL); #endif +#if OPENSSL_VERSION_NUMBER >=0x10100000L + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS | + OPENSSL_INIT_LOAD_CONFIG | OPENSSL_INIT_ENGINE_OPENSSL | OPENSSL_INIT_ENGINE_ALL_BUILTIN , NULL); + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); +#endif + #if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_load_error_strings(); SSL_library_init(); diff --git a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp index 978f0ca6..bbda81b3 100644 --- a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp +++ b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp @@ -104,7 +104,7 @@ TLSSocket_OpenSSL::TLSSocket_OpenSSL( m_connected(false), m_ssl(0), m_status(0), - m_ex() { + m_ex(nullptr) { } @@ -309,7 +309,7 @@ size_t TLSSocket_OpenSSL::receiveRaw(byte_t* buffer, const size_t count) { ERR_clear_error(); int rc = SSL_read(m_ssl, buffer, static_cast <int>(count)); - if (m_ex.get()) { + if (!!m_ex) { internalThrow(); } @@ -379,7 +379,7 @@ size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t ERR_clear_error(); int rc = SSL_write(m_ssl, buffer, static_cast <int>(count)); - if (m_ex.get()) { + if (!!m_ex) { internalThrow(); } @@ -511,8 +511,8 @@ shared_ptr <security::cert::certificateChain> TLSSocket_OpenSSL::getPeerCertific void TLSSocket_OpenSSL::internalThrow() { - if (m_ex.get()) { - throw *m_ex; + if (!!m_ex) { + std::rethrow_exception(m_ex); } } @@ -630,7 +630,7 @@ int TLSSocket_OpenSSL::bio_write(BIO* bio, const char* buf, int len) { } catch (exception& e) { // Workaround for passing C++ exceptions from C BIO functions - sok->m_ex.reset(e.clone()); + sok->m_ex = std::current_exception(); return -1; } } @@ -667,7 +667,7 @@ int TLSSocket_OpenSSL::bio_read(BIO* bio, char* buf, int len) { } catch (exception& e) { // Workaround for passing C++ exceptions from C BIO functions - sok->m_ex.reset(e.clone()); + sok->m_ex = std::current_exception(); return -1; } } diff --git a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp index e30df680..62fd5f0d 100644 --- a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp +++ b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp @@ -125,7 +125,7 @@ private: unsigned int m_status; // Last exception thrown from C BIO functions - scoped_ptr <exception> m_ex; + exception_ptr m_ex; }; diff --git a/src/vmime/security/cert/defaultCertificateVerifier.cpp b/src/vmime/security/cert/defaultCertificateVerifier.cpp index 33162a0a..c41396f1 100644 --- a/src/vmime/security/cert/defaultCertificateVerifier.cpp +++ b/src/vmime/security/cert/defaultCertificateVerifier.cpp @@ -75,6 +75,69 @@ void defaultCertificateVerifier::verify( } +void defaultCertificateVerifier::verifyX509chain + (shared_ptr <certificateChain> chain, size_t chainLen) +{ + // For every certificate in the chain, verify that the certificate + // has been issued by the next certificate in the chain + if (chainLen >= 2) { + for (size_t i = 0 ; i < chainLen - 1 ; ++i) { + shared_ptr <X509Certificate> cert = dynamicCast <X509Certificate>(chain->getAt(i)); + shared_ptr <X509Certificate> next = dynamicCast <X509Certificate>(chain->getAt(i + 1)); + + if (!cert->checkIssuer(next)) { + certificateIssuerVerificationException ex; + ex.setCertificate(cert); + + throw ex; + } + } + } + + // For every certificate in the chain, verify that the certificate + // is valid at the current time + for (size_t i = 0 ; i < chainLen ; ++i) { + shared_ptr <X509Certificate> cert = dynamicCast <X509Certificate>(chain->getAt(i)); + cert->checkValidity(); + } + + // 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 + shared_ptr <X509Certificate> lastCert = dynamicCast <X509Certificate>(chain->getAt(chainLen - 1)); + + bool trusted = false; + + for (size_t i = 0 ; !trusted && i < m_x509RootCAs.size() ; ++i) { + shared_ptr <X509Certificate> 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 + shared_ptr <X509Certificate> firstCert = dynamicCast <X509Certificate>(chain->getAt(0)); + + for (size_t i = 0 ; !trusted && i < m_x509TrustedCerts.size() ; ++i) { + shared_ptr <X509Certificate> cert = m_x509TrustedCerts[i]; + + if (firstCert->equals(cert)) { + trusted = true; + } + } + + if (!trusted) { + certificateNotTrustedException ex; + ex.setCertificate(firstCert); + + throw ex; + } +} + void defaultCertificateVerifier::verifyX509( const shared_ptr <certificateChain>& chain, const string& hostname @@ -161,6 +224,44 @@ void defaultCertificateVerifier::verifyX509( throw ex; } + + // Perform verification on all possible subchains in order to work + // around a server that sends extranous certificates in the chain + // after the valid one. + // + // Note: The TLS 1.3 draft says the following: + // + // Note: Prior to TLS 1.3, "certificate_list" ordering required each + // certificate to certify the one immediately preceding it; however, + // some implementations allowed some flexibility. Servers sometimes + // send both a current and deprecated intermediate for transitional + // purposes, and others are simply configured incorrectly, but these + // cases can nonetheless be validated properly. For maximum + // compatibility, all implementations SHOULD be prepared to handle + // potentially extraneous certificates and arbitrary orderings from any + // TLS version, with the exception of the end-entity certificate which + // MUST be first. + // + // This code does NOT yet handle arbitrary ordering. + // + for (size_t chainLen = chain->getCount(); chainLen > 0; chainLen--) { + + try { + verifyX509chain(chain, chainLen); + return; + } + catch(...) { + if (chainLen == 1) { + throw; + } + } + } + + // We really should never get here from the above loop + certificateNotTrustedException ex; + ex.setCertificate(firstCert); + + throw ex; } diff --git a/src/vmime/security/cert/defaultCertificateVerifier.hpp b/src/vmime/security/cert/defaultCertificateVerifier.hpp index 4aa4445c..b90283b2 100644 --- a/src/vmime/security/cert/defaultCertificateVerifier.hpp +++ b/src/vmime/security/cert/defaultCertificateVerifier.hpp @@ -74,6 +74,13 @@ public: private: /** Verify a chain of X.509 certificates. + * + * @param chain list of X.509 certificates + * @param chainLen number of certificates to verify + */ + void verifyX509chain(shared_ptr <certificateChain> chain, size_t chainLen); + + /** Verify a chain of X.509 certificates. * * @param chain list of X.509 certificates * @param hostname server hostname diff --git a/src/vmime/security/sasl/builtinSASLMechanism.cpp b/src/vmime/security/sasl/builtinSASLMechanism.cpp index 846e2ccf..020cae95 100644 --- a/src/vmime/security/sasl/builtinSASLMechanism.cpp +++ b/src/vmime/security/sasl/builtinSASLMechanism.cpp @@ -78,6 +78,10 @@ bool builtinSASLMechanism::step( char* output = 0; size_t outputLen = 0; + if (nullptr == sess->m_gsaslSession) { + throw exceptions::sasl_exception("Invalid SASL session"); + } + const int result = gsasl_step( sess->m_gsaslSession, reinterpret_cast <const char*>(challenge), challengeLen, diff --git a/tests/net/imap/IMAPParserTest.cpp b/tests/net/imap/IMAPParserTest.cpp index 387e4c66..47880a06 100644 --- a/tests/net/imap/IMAPParserTest.cpp +++ b/tests/net/imap/IMAPParserTest.cpp @@ -39,6 +39,7 @@ VMIME_TEST_SUITE_BEGIN(IMAPParserTest) VMIME_TEST(testPipelining) VMIME_TEST(testStarFlagWithoutBackslash) VMIME_TEST(testUnquotedMailboxName) + VMIME_TEST(testInvalidCharsInAstring) VMIME_TEST_LIST_END @@ -427,4 +428,59 @@ VMIME_TEST_SUITE_BEGIN(IMAPParserTest) } } + // Some broken IMAP servers return non-ASCII chars in astring. + // Server returns UTF-8 instead of modified UTF-7. + void testInvalidCharsInAstring() { + + const char* respText = + R"END(* STATUS Segregator/Społeczności (MESSAGES 3 UIDNEXT 4 UIDVALIDITY 1519867193 UNSEEN 0))END" + "\r\n" + R"END(a001 OK Completed.)END" + "\r\n"; + + // Strict mode + { + auto socket = vmime::make_shared <testSocket>(); + auto toh = vmime::make_shared <testTimeoutHandler>(); + + auto tag = vmime::make_shared <vmime::net::imap::IMAPTag>(); + + socket->localSend(respText); + + auto parser = vmime::make_shared <vmime::net::imap::IMAPParser>(); + + parser->setSocket(socket); + parser->setTimeoutHandler(toh); + parser->setStrict(true); + + VASSERT_THROW("strict mode", parser->readResponse(*tag), vmime::exceptions::invalid_response); + } + + // Non-strict mode + { + auto socket = vmime::make_shared <testSocket>(); + auto toh = vmime::make_shared <testTimeoutHandler>(); + + auto tag = vmime::make_shared <vmime::net::imap::IMAPTag>(); + + socket->localSend(respText); + + auto parser = vmime::make_shared <vmime::net::imap::IMAPParser>(); + + parser->setSocket(socket); + parser->setTimeoutHandler(toh); + parser->setStrict(false); + + std::unique_ptr <vmime::net::imap::IMAPParser::response> resp; + + VASSERT_NO_THROW("non-strict mode", resp.reset(parser->readResponse(*tag))); + + VASSERT_EQ("resp size", 1, resp->continue_req_or_response_data.size()); + VASSERT("resp data", resp->continue_req_or_response_data[0]->response_data); + VASSERT("mbox data", resp->continue_req_or_response_data[0]->response_data->mailbox_data); + VASSERT("mbox", resp->continue_req_or_response_data[0]->response_data->mailbox_data->mailbox); + VASSERT_EQ("mbox name", "Segregator/Społeczności", resp->continue_req_or_response_data[0]->response_data->mailbox_data->mailbox->name); + } + } + VMIME_TEST_SUITE_END |