diff options
Diffstat (limited to 'src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp')
-rw-r--r-- | src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp | 516 |
1 files changed, 267 insertions, 249 deletions
diff --git a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp index afc7e514..db782bb2 100644 --- a/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp +++ b/src/vmime/net/tls/openssl/TLSSocket_OpenSSL.cpp @@ -1,6 +1,6 @@ // // VMime library (http://www.vmime.org) -// Copyright (C) 2002-2013 Vincent Richard <[email protected]> +// Copyright (C) 2002 Vincent Richard <[email protected]> // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -55,8 +55,7 @@ static OpenSSLInitializer::autoInitializer openSSLInitializer; #if OPENSSL_VERSION_NUMBER < 0x10100000L // static -BIO_METHOD TLSSocket_OpenSSL::sm_customBIOMethod = -{ +BIO_METHOD TLSSocket_OpenSSL::sm_customBIOMethod = { 100 | BIO_TYPE_SOURCE_SINK, "vmime::socket glue", TLSSocket_OpenSSL::bio_write, @@ -87,36 +86,42 @@ BIO_METHOD TLSSocket_OpenSSL::sm_customBIOMethod = // static -shared_ptr <TLSSocket> TLSSocket::wrap(const shared_ptr <TLSSession>& session, const shared_ptr <socket>& sok) -{ - return make_shared <TLSSocket_OpenSSL> - (dynamicCast <TLSSession_OpenSSL>(session), sok); +shared_ptr <TLSSocket> TLSSocket::wrap( + const shared_ptr <TLSSession>& session, + const shared_ptr <socket>& sok +) { + + return make_shared <TLSSocket_OpenSSL>(dynamicCast <TLSSession_OpenSSL>(session), sok); } -TLSSocket_OpenSSL::TLSSocket_OpenSSL(const shared_ptr <TLSSession_OpenSSL>& session, const shared_ptr <socket>& sok) - : m_session(session), m_wrapped(sok), m_connected(false), m_ssl(0), m_status(0), m_ex() -{ +TLSSocket_OpenSSL::TLSSocket_OpenSSL( + const shared_ptr <TLSSession_OpenSSL>& session, + const shared_ptr <socket>& sok +) + : m_session(session), + m_wrapped(sok), + m_connected(false), + m_ssl(0), + m_status(0), + m_ex() { + } -TLSSocket_OpenSSL::~TLSSocket_OpenSSL() -{ - try - { +TLSSocket_OpenSSL::~TLSSocket_OpenSSL() { + + try { disconnect(); - } - catch (...) - { + } catch (...) { // Don't throw in destructor } } -void TLSSocket_OpenSSL::createSSLHandle() -{ - if (m_wrapped->isConnected()) - { +void TLSSocket_OpenSSL::createSSLHandle() { + + if (m_wrapped->isConnected()) { #if OPENSSL_VERSION_NUMBER < 0x10100000L @@ -128,8 +133,7 @@ void TLSSocket_OpenSSL::createSSLHandle() BIO_METHOD* bioMeth = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "vmime::socket glue"); - if (!bioMeth) - { + if (!bioMeth) { BIO_meth_free(bioMeth); throw exceptions::tls_exception("BIO_meth_new() failed"); } @@ -147,15 +151,13 @@ void TLSSocket_OpenSSL::createSSLHandle() #endif - if (!sockBio) - { + if (!sockBio) { throw exceptions::tls_exception("BIO_new() failed"); } m_ssl = SSL_new(m_session->getContext()); - if (!m_ssl) - { + if (!m_ssl) { BIO_free(sockBio); throw exceptions::tls_exception("Cannot create SSL object"); } @@ -163,156 +165,156 @@ void TLSSocket_OpenSSL::createSSLHandle() SSL_set_bio(m_ssl, sockBio, sockBio); SSL_set_connect_state(m_ssl); SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - } - else - { + + } else { + throw exceptions::tls_exception("Unconnected socket error"); } } -void TLSSocket_OpenSSL::connect(const string& address, const port_t port) -{ - try - { +void TLSSocket_OpenSSL::connect(const string& address, const port_t port) { + + try { + m_wrapped->connect(address, port); createSSLHandle(); handshake(); - } - catch (...) - { + + } catch (...) { + disconnect(); throw; } } -void TLSSocket_OpenSSL::disconnect() -{ - if (m_ssl) - { +void TLSSocket_OpenSSL::disconnect() { + + if (m_ssl) { + // Don't shut down the socket more than once. int shutdownState = SSL_get_shutdown(m_ssl); bool shutdownSent = (shutdownState & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN; - if (!shutdownSent) + if (!shutdownSent) { SSL_shutdown(m_ssl); + } SSL_free(m_ssl); m_ssl = 0; } - if (m_connected) - { + if (m_connected) { m_connected = false; m_wrapped->disconnect(); } } -bool TLSSocket_OpenSSL::isConnected() const -{ +bool TLSSocket_OpenSSL::isConnected() const { + return m_wrapped->isConnected() && m_connected; } -size_t TLSSocket_OpenSSL::getBlockSize() const -{ +size_t TLSSocket_OpenSSL::getBlockSize() const { + return 16384; // 16 KB } -const string TLSSocket_OpenSSL::getPeerName() const -{ +const string TLSSocket_OpenSSL::getPeerName() const { + return m_wrapped->getPeerName(); } -const string TLSSocket_OpenSSL::getPeerAddress() const -{ +const string TLSSocket_OpenSSL::getPeerAddress() const { + return m_wrapped->getPeerAddress(); } -shared_ptr <timeoutHandler> TLSSocket_OpenSSL::getTimeoutHandler() -{ +shared_ptr <timeoutHandler> TLSSocket_OpenSSL::getTimeoutHandler() { + return m_wrapped->getTimeoutHandler(); } -void TLSSocket_OpenSSL::setTracer(const shared_ptr <net::tracer>& tracer) -{ +void TLSSocket_OpenSSL::setTracer(const shared_ptr <net::tracer>& tracer) { + m_wrapped->setTracer(tracer); } -shared_ptr <net::tracer> TLSSocket_OpenSSL::getTracer() -{ +shared_ptr <net::tracer> TLSSocket_OpenSSL::getTracer() { + return m_wrapped->getTracer(); } -bool TLSSocket_OpenSSL::waitForRead(const int msecs) -{ +bool TLSSocket_OpenSSL::waitForRead(const int msecs) { + return m_wrapped->waitForRead(msecs); } -bool TLSSocket_OpenSSL::waitForWrite(const int msecs) -{ +bool TLSSocket_OpenSSL::waitForWrite(const int msecs) { + return m_wrapped->waitForWrite(msecs); } -void TLSSocket_OpenSSL::receive(string& buffer) -{ +void TLSSocket_OpenSSL::receive(string& buffer) { + const size_t size = receiveRaw(m_buffer, sizeof(m_buffer)); - if (size != 0) + if (size != 0) { buffer = utility::stringUtils::makeStringFromBytes(m_buffer, size); - else + } else { buffer.clear(); + } } -void TLSSocket_OpenSSL::send(const string& buffer) -{ +void TLSSocket_OpenSSL::send(const string& buffer) { + sendRaw(reinterpret_cast <const byte_t*>(buffer.data()), buffer.length()); } -void TLSSocket_OpenSSL::send(const char* str) -{ +void TLSSocket_OpenSSL::send(const char* str) { + sendRaw(reinterpret_cast <const byte_t*>(str), ::strlen(str)); } -size_t TLSSocket_OpenSSL::receiveRaw(byte_t* buffer, const size_t count) -{ - if (!m_ssl) +size_t TLSSocket_OpenSSL::receiveRaw(byte_t* buffer, const size_t count) { + + if (!m_ssl) { throw exceptions::socket_not_connected_exception(); + } m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); ERR_clear_error(); int rc = SSL_read(m_ssl, buffer, static_cast <int>(count)); - if (m_ex.get()) + if (m_ex.get()) { internalThrow(); + } + + if (rc <= 0) { - if (rc <= 0) - { int error = SSL_get_error(m_ssl, rc); - if (error == SSL_ERROR_WANT_WRITE) - { + if (error == SSL_ERROR_WANT_WRITE) { m_status |= STATUS_WANT_WRITE; return 0; - } - else if (error == SSL_ERROR_WANT_READ) - { + } else if (error == SSL_ERROR_WANT_READ) { m_status |= STATUS_WANT_READ; return 0; } @@ -324,37 +326,35 @@ size_t TLSSocket_OpenSSL::receiveRaw(byte_t* buffer, const size_t count) } -void TLSSocket_OpenSSL::sendRaw(const byte_t* buffer, const size_t count) -{ - if (!m_ssl) +void TLSSocket_OpenSSL::sendRaw(const byte_t* buffer, const size_t count) { + + if (!m_ssl) { throw exceptions::socket_not_connected_exception(); + } m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); - for (size_t size = count ; size > 0 ; ) - { + for (size_t size = count ; size > 0 ; ) { + ERR_clear_error(); int rc = SSL_write(m_ssl, buffer, static_cast <int>(size)); - if (rc <= 0) - { + if (rc <= 0) { + int error = SSL_get_error(m_ssl, rc); - if (error == SSL_ERROR_WANT_READ) - { + if (error == SSL_ERROR_WANT_READ) { m_wrapped->waitForRead(); continue; - } - else if (error == SSL_ERROR_WANT_WRITE) - { + } else if (error == SSL_ERROR_WANT_WRITE) { m_wrapped->waitForWrite(); continue; } handleError(rc); - } - else - { + + } else { + buffer += rc; size -= rc; } @@ -362,30 +362,29 @@ void TLSSocket_OpenSSL::sendRaw(const byte_t* buffer, const size_t count) } -size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t count) -{ - if (!m_ssl) +size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t count) { + + if (!m_ssl) { throw exceptions::socket_not_connected_exception(); + } m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); ERR_clear_error(); int rc = SSL_write(m_ssl, buffer, static_cast <int>(count)); - if (m_ex.get()) + if (m_ex.get()) { internalThrow(); + } + + if (rc <= 0) { - if (rc <= 0) - { int error = SSL_get_error(m_ssl, rc); - if (error == SSL_ERROR_WANT_WRITE) - { + if (error == SSL_ERROR_WANT_WRITE) { m_status |= STATUS_WANT_WRITE; return 0; - } - else if (error == SSL_ERROR_WANT_READ) - { + } else if (error == SSL_ERROR_WANT_READ) { m_status |= STATUS_WANT_READ; return 0; } @@ -397,59 +396,65 @@ size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t } -void TLSSocket_OpenSSL::handshake() -{ +void TLSSocket_OpenSSL::handshake() { + shared_ptr <timeoutHandler> toHandler = m_wrapped->getTimeoutHandler(); - if (toHandler) + if (toHandler) { toHandler->resetTimeOut(); + } - if (getTracer()) + if (getTracer()) { getTracer()->traceSend("Beginning SSL/TLS handshake"); + } // Start handshaking process - if (!m_ssl) + if (!m_ssl) { createSSLHandle(); + } + + try { - try - { int rc; ERR_clear_error(); - while ((rc = SSL_do_handshake(m_ssl)) <= 0) - { + while ((rc = SSL_do_handshake(m_ssl)) <= 0) { + const int err = SSL_get_error(m_ssl, rc); - if (err == SSL_ERROR_WANT_READ) + if (err == SSL_ERROR_WANT_READ) { m_wrapped->waitForRead(); - else if (err == SSL_ERROR_WANT_WRITE) + } else if (err == SSL_ERROR_WANT_WRITE) { m_wrapped->waitForWrite(); - else + } else { handleError(rc); + } // Check whether the time-out delay is elapsed - if (toHandler && toHandler->isTimeOut()) - { - if (!toHandler->handleTimeOut()) + if (toHandler && toHandler->isTimeOut()) { + + if (!toHandler->handleTimeOut()) { throw exceptions::operation_timed_out(); + } toHandler->resetTimeOut(); } ERR_clear_error(); } - } - catch (...) - { + + } catch (...) { + throw; } // Verify server's certificate(s) shared_ptr <security::cert::certificateChain> certs = getPeerCertificates(); - if (certs == NULL) + if (!certs) { throw exceptions::tls_exception("No peer certificate."); + } m_session->getCertificateVerifier()->verify(certs, getPeerName()); @@ -457,121 +462,130 @@ void TLSSocket_OpenSSL::handshake() } -shared_ptr <security::cert::certificateChain> TLSSocket_OpenSSL::getPeerCertificates() -{ - if (getTracer()) +shared_ptr <security::cert::certificateChain> TLSSocket_OpenSSL::getPeerCertificates() { + + if (getTracer()) { getTracer()->traceSend("Getting peer certificates"); + } STACK_OF(X509)* chain = SSL_get_peer_cert_chain(m_ssl); - if (chain == NULL) + if (chain == NULL) { return null; + } int certCount = sk_X509_num(chain); - if (certCount == 0) + if (certCount == 0) { return null; + } bool error = false; std::vector <shared_ptr <security::cert::certificate> > certs; - for (int i = 0; i < certCount && !error; i++) - { + for (int i = 0; i < certCount && !error; i++) { + shared_ptr <vmime::security::cert::X509Certificate> cert = vmime::security::cert::X509Certificate_OpenSSL::importInternal(sk_X509_value(chain, i)); - if (cert) + if (cert) { certs.push_back(cert); - else + } else { error = true; + } } - if (error) + if (error) { return null; + } return make_shared <security::cert::certificateChain>(certs); } -void TLSSocket_OpenSSL::internalThrow() -{ - if (m_ex.get()) +void TLSSocket_OpenSSL::internalThrow() { + + if (m_ex.get()) { throw *m_ex; + } } -void TLSSocket_OpenSSL::handleError(int rc) -{ - if (rc > 0) return; +void TLSSocket_OpenSSL::handleError(int rc) { + + if (rc > 0) { + return; + } internalThrow(); int sslError = SSL_get_error(m_ssl, rc); long lastError = ERR_get_error(); - switch (sslError) - { - case SSL_ERROR_ZERO_RETURN: + switch (sslError) { - disconnect(); - return; + case SSL_ERROR_ZERO_RETURN: - case SSL_ERROR_SYSCALL: - { - if (lastError == 0) - { - if (rc == 0) - { - throw exceptions::tls_exception("SSL connection unexpectedly closed"); - } - else - { - std::ostringstream oss; - oss << "The BIO reported an error: " << rc; - oss.flush(); - throw exceptions::tls_exception(oss.str()); + disconnect(); + return; + + case SSL_ERROR_SYSCALL: { + + if (lastError == 0) { + + if (rc == 0) { + + throw exceptions::tls_exception("SSL connection unexpectedly closed"); + + } else { + + std::ostringstream oss; + oss << "The BIO reported an error: " << rc; + oss.flush(); + throw exceptions::tls_exception(oss.str()); + } } + + break; } - break; - } - case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_READ: - BIO_set_retry_read(SSL_get_rbio(m_ssl)); - break; + BIO_set_retry_read(SSL_get_rbio(m_ssl)); + break; - case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_WRITE: - BIO_set_retry_write(SSL_get_wbio(m_ssl)); - break; + BIO_set_retry_write(SSL_get_wbio(m_ssl)); + break; - // This happens only for BIOs of type BIO_s_connect() or BIO_s_accept() - case SSL_ERROR_WANT_CONNECT: - case SSL_ERROR_WANT_ACCEPT: - // SSL_CTX_set_client_cert_cb related, not used - case SSL_ERROR_WANT_X509_LOOKUP: - case SSL_ERROR_SSL: - default: + // This happens only for BIOs of type BIO_s_connect() or BIO_s_accept() + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + // SSL_CTX_set_client_cert_cb related, not used + case SSL_ERROR_WANT_X509_LOOKUP: + case SSL_ERROR_SSL: + default: - if (lastError == 0) - { - throw exceptions::tls_exception("Unexpected SSL IO error"); - } - else - { - char buffer[256]; - ERR_error_string_n(lastError, buffer, sizeof(buffer)); - vmime::string msg(buffer); - throw exceptions::tls_exception(msg); - } + if (lastError == 0) { + + throw exceptions::tls_exception("Unexpected SSL IO error"); + + } else { - break; + char buffer[256]; + ERR_error_string_n(lastError, buffer, sizeof(buffer)); + vmime::string msg(buffer); + throw exceptions::tls_exception(msg); + } + + break; } } -unsigned int TLSSocket_OpenSSL::getStatus() const -{ +unsigned int TLSSocket_OpenSSL::getStatus() const { + return m_status; } @@ -580,33 +594,35 @@ unsigned int TLSSocket_OpenSSL::getStatus() const // static -int TLSSocket_OpenSSL::bio_write(BIO* bio, const char* buf, int len) -{ +int TLSSocket_OpenSSL::bio_write(BIO* bio, const char* buf, int len) { + BIO_clear_retry_flags(bio); - if (buf == NULL || len <= 0) + if (buf == NULL || len <= 0) { return -1; + } TLSSocket_OpenSSL *sok = reinterpret_cast <TLSSocket_OpenSSL*>(BIO_get_data(bio)); - if (!BIO_get_init(bio) || !sok) + if (!BIO_get_init(bio) || !sok) { return -1; + } + + try { - try - { - const size_t n = sok->m_wrapped->sendRawNonBlocking - (reinterpret_cast <const byte_t*>(buf), len); + const size_t n = sok->m_wrapped->sendRawNonBlocking( + reinterpret_cast <const byte_t*>(buf), len + ); - if (n == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) - { + if (n == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) { BIO_set_retry_write(bio); return -1; } return static_cast <int>(n); - } - catch (exception& e) - { + + } catch (exception& e) { + // Workaround for passing C++ exceptions from C BIO functions sok->m_ex.reset(e.clone()); return -1; @@ -615,33 +631,35 @@ int TLSSocket_OpenSSL::bio_write(BIO* bio, const char* buf, int len) // static -int TLSSocket_OpenSSL::bio_read(BIO* bio, char* buf, int len) -{ +int TLSSocket_OpenSSL::bio_read(BIO* bio, char* buf, int len) { + BIO_clear_retry_flags(bio); - if (buf == NULL || len <= 0) + if (buf == NULL || len <= 0) { return -1; + } TLSSocket_OpenSSL *sok = reinterpret_cast <TLSSocket_OpenSSL*>(BIO_get_data(bio)); - if (!BIO_get_init(bio) || !sok) + if (!BIO_get_init(bio) || !sok) { return -1; + } + + try { - try - { - const size_t n = sok->m_wrapped->receiveRaw - (reinterpret_cast <byte_t*>(buf), len); + const size_t n = sok->m_wrapped->receiveRaw( + reinterpret_cast <byte_t*>(buf), len + ); - if (n == 0 || sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) - { + if (n == 0 || sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) { BIO_set_retry_read(bio); return -1; } return static_cast <int>(n); - } - catch (exception& e) - { + + } catch (exception& e) { + // Workaround for passing C++ exceptions from C BIO functions sok->m_ex.reset(e.clone()); return -1; @@ -650,50 +668,50 @@ int TLSSocket_OpenSSL::bio_read(BIO* bio, char* buf, int len) // static -int TLSSocket_OpenSSL::bio_puts(BIO* bio, const char* str) -{ +int TLSSocket_OpenSSL::bio_puts(BIO* bio, const char* str) { + return bio_write(bio, str, static_cast <int>(strlen(str))); } // static -long TLSSocket_OpenSSL::bio_ctrl(BIO* bio, int cmd, long num, void* /* ptr */) -{ +long TLSSocket_OpenSSL::bio_ctrl(BIO* bio, int cmd, long num, void* /* ptr */) { + long ret = 1; - switch (cmd) - { - case BIO_CTRL_INFO: + switch (cmd) { + + case BIO_CTRL_INFO: - ret = 0; - break; + ret = 0; + break; - case BIO_CTRL_GET_CLOSE: + case BIO_CTRL_GET_CLOSE: - ret = BIO_get_shutdown(bio); - break; + ret = BIO_get_shutdown(bio); + break; - case BIO_CTRL_SET_CLOSE: + case BIO_CTRL_SET_CLOSE: - BIO_set_shutdown(bio, static_cast <int>(num)); - break; + BIO_set_shutdown(bio, static_cast <int>(num)); + break; - case BIO_CTRL_PENDING: - case BIO_CTRL_WPENDING: + case BIO_CTRL_PENDING: + case BIO_CTRL_WPENDING: - ret = 0; - break; + ret = 0; + break; - case BIO_CTRL_DUP: - case BIO_CTRL_FLUSH: + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: - ret = 1; - break; + ret = 1; + break; - default: + default: - ret = 0; - break; + ret = 0; + break; } return ret; @@ -701,8 +719,8 @@ long TLSSocket_OpenSSL::bio_ctrl(BIO* bio, int cmd, long num, void* /* ptr */) // static -int TLSSocket_OpenSSL::bio_create(BIO* bio) -{ +int TLSSocket_OpenSSL::bio_create(BIO* bio) { + BIO_set_init(bio, 0); BIO_set_num(bio, 0); BIO_set_data(bio, NULL); @@ -713,13 +731,13 @@ int TLSSocket_OpenSSL::bio_create(BIO* bio) // static -int TLSSocket_OpenSSL::bio_destroy(BIO* bio) -{ - if (bio == NULL) +int TLSSocket_OpenSSL::bio_destroy(BIO* bio) { + + if (!bio) { return 0; + } - if (BIO_get_shutdown(bio)) - { + if (BIO_get_shutdown(bio)) { BIO_set_data(bio, NULL); BIO_set_init(bio, 0); BIO_set_flags(bio, 0); |