summaryrefslogtreecommitdiff
path: root/cpp/src/IceSSL/SslTransceiver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceSSL/SslTransceiver.cpp')
-rw-r--r--cpp/src/IceSSL/SslTransceiver.cpp995
1 files changed, 478 insertions, 517 deletions
diff --git a/cpp/src/IceSSL/SslTransceiver.cpp b/cpp/src/IceSSL/SslTransceiver.cpp
index 501fb30248e..b23974e1ebf 100644
--- a/cpp/src/IceSSL/SslTransceiver.cpp
+++ b/cpp/src/IceSSL/SslTransceiver.cpp
@@ -9,23 +9,16 @@
#include <Ice/LoggerUtil.h>
#include <Ice/Stats.h>
-#include <Ice/Buffer.h>
#include <Ice/Network.h>
#include <Ice/LocalException.h>
-#include <Ice/Properties.h>
-#include <IceSSL/OpenSSL.h>
#include <IceSSL/SslTransceiver.h>
-#include <IceSSL/OpenSSLPluginI.h>
#include <IceSSL/TraceLevels.h>
#include <IceSSL/Exception.h>
#include <IceSSL/OpenSSLPluginI.h>
-#include <IceSSL/CertificateVerifierOpenSSL.h>
#include <IceSSL/OpenSSLUtils.h>
#include <openssl/err.h>
-#include <sstream>
-
using namespace std;
using namespace Ice;
using namespace IceInternal;
@@ -63,19 +56,12 @@ IceSSL::SslTransceiver::close()
try
{
- int shutdown = 0;
- int numRetries = 100;
- int retries = -numRetries;
- do
- {
- shutdown = internalShutdownWrite(0);
- retries++;
- }
- while((shutdown == 0) && (retries < 0));
+ internalShutdownWrite(10 * 1000);
}
- catch(...)
+ catch(const Ice::Exception& e)
{
- // Ignore, close() should not throw exceptions.
+ Warning warn(_logger);
+ warn << "error in shutting down ssl connection " << e;
}
assert(_fd != INVALID_SOCKET);
@@ -100,15 +86,15 @@ IceSSL::SslTransceiver::shutdownWrite()
out << "shutting down ssl connection for writing\n" << toString();
}
- int shutdown = 0;
- int numRetries = 100;
- int retries = -numRetries;
- do
+ try
{
- shutdown = internalShutdownWrite(0);
- retries++;
+ internalShutdownWrite(10 * 1000);
+ }
+ catch(const Ice::Exception& e)
+ {
+ Warning warn(_logger);
+ warn << "error in shutting down ssl connection " << e;
}
- while((shutdown == 0) && (retries < 0));
assert(_fd != INVALID_SOCKET);
shutdownSocketWrite(_fd);
@@ -123,212 +109,267 @@ IceSSL::SslTransceiver::shutdownReadWrite()
out << "shutting down ssl connection for reading and writing\n" << toString();
}
-/*
- int shutdown = 0;
- int numRetries = 100;
- int retries = -numRetries;
- do
- {
- shutdown = internalShutdownWrite(0);
- retries++;
- }
- while((shutdown == 0) && (retries < 0));
-*/
-
assert(_fd != INVALID_SOCKET);
shutdownSocketReadWrite(_fd);
}
void
-IceSSL::SslTransceiver::read(Buffer& buf, int timeout)
+IceSSL::SslTransceiver::write(Buffer& buf, int timeout)
{
assert(_fd != INVALID_SOCKET);
_plugin->registerThread();
+
Buffer::Container::difference_type packetSize =
static_cast<Buffer::Container::difference_type>(buf.b.end() - buf.i);
- int totalBytesRead = 0;
- int bytesRead;
- int initReturn = 0;
+#ifdef _WIN32
+ //
+ // Limit packet size to avoid performance problems on WIN32.
+ //
+ if(packetSize > 64 * 1024)
+ {
+ packetSize = 64 * 1024;
+ }
+#endif
- // We keep reading until we're done.
+ // We keep writing until we're done.
while(buf.i != buf.b.end())
{
- // Ensure we're initialized.
- initReturn = initialize(timeout);
-
- if(initReturn == -1)
+ ERR_clear_error();
+ int ret = SSL_write(_sslConnection, &*buf.i, static_cast<int>(packetSize));
+ switch(SSL_get_error(_sslConnection, ret))
{
- // Handshake underway, timeout immediately, easy way to deal with this.
- throw TimeoutException(__FILE__, __LINE__);
- }
+ case SSL_ERROR_NONE:
+ break;
+
+ case SSL_ERROR_WANT_WRITE:
+ {
+ writeSelect(timeout);
+ break;
+ }
+
+ //
+ // If session renegotiation is ever enabled this could
+ // occur.
+ //
+ //case SSL_ERROR_WANT_READ:
+ //{
+ // readSelect(timeout);
+ // break;
+ //}
- if(initReturn == 0)
- {
- // Retry the initialize call
- continue;
- }
+ case SSL_ERROR_SYSCALL:
+ {
+ if(ret == -1)
+ {
+ // IO Error in underlying BIO
- // initReturn must be > 0, so we're okay to try a read
+ if(interrupted())
+ {
+ continue;
+ }
- if(!pending() && !readSelect(_readTimeout))
- {
- // Nothing is left to read (according to SSL).
- if(_traceLevels->security >= IceSSL::SECURITY_PROTOCOL)
+ if(noBuffers() && packetSize > 1024)
+ {
+ packetSize /= 2;
+ continue;
+ }
+
+ //
+ // Its not clear whether this can occur, isn't
+ // this the same as SSL_ERROR_WANT_WRITE?
+ //
+ if(wouldBlock())
+ {
+ writeSelect(timeout);
+ continue;
+ }
+
+ if(connectionLost())
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ }
+ // fall through
+ }
+
+ case SSL_ERROR_ZERO_RETURN:
{
- Trace out(_logger, _traceLevels->securityCat);
- out << "no pending application-level bytes";
+ assert(ret == 0);
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = 0;
+ throw ex;
}
- // We're done here.
- break;
+ case SSL_ERROR_SSL:
+ {
+ ProtocolException ex(__FILE__, __LINE__);
+ ex.message = "encountered a violation of the ssl protocol\n";
+ ex.message += IceSSL::sslGetErrors();
+ throw ex;
+ }
+
+ default:
+ {
+ Warning warn(_logger);
+ warn << "IceSSL: unexpected result from SSL_write: " << SSL_get_error(_sslConnection, ret);
+ }
}
- _readTimeout = timeout;
+ if(_traceLevels->network >= 3)
+ {
+ Trace out(_logger, _traceLevels->networkCat);
+ out << "sent " << ret << " of " << packetSize; out << " bytes via ssl\n"
+ << fdToString(SSL_get_fd(_sslConnection));
+ }
- bytesRead = sslRead(&*buf.i, static_cast<Int>(packetSize));
+ if(_stats)
+ {
+ _stats->bytesSent(type(), ret);
+ }
- switch(getLastError())
- {
- case SSL_ERROR_NONE:
- {
- if(bytesRead > 0)
- {
- if(_traceLevels->network >= 3)
- {
- Trace out(_logger, _traceLevels->networkCat);
- out << "received " << bytesRead << " of " << packetSize;
- out << " bytes via ssl\n" << toString();
- }
+ buf.i += ret;
- if(_stats)
- {
- _stats->bytesReceived(type(), bytesRead);
- }
+ if(packetSize > buf.b.end() - buf.i)
+ {
+ packetSize = static_cast<Buffer::Container::difference_type>(buf.b.end() - buf.i);
+ }
+ }
+}
- totalBytesRead += bytesRead;
+void
+IceSSL::SslTransceiver::read(Buffer& buf, int timeout)
+{
+ assert(_fd != INVALID_SOCKET);
- buf.i += bytesRead;
+ _plugin->registerThread();
- if(packetSize > buf.b.end() - buf.i)
- {
- packetSize = static_cast<Buffer::Container::difference_type>(buf.b.end() - buf.i);
- }
- }
- continue;
- }
+ Buffer::Container::difference_type packetSize =
+ static_cast<Buffer::Container::difference_type>(buf.b.end() - buf.i);
+
+ while(buf.i != buf.b.end())
+ {
+ ERR_clear_error();
+ int ret = SSL_read(_sslConnection, &*buf.i, static_cast<Int>(packetSize));
+ switch(SSL_get_error(_sslConnection, ret))
+ {
+ case SSL_ERROR_NONE:
+ break;
case SSL_ERROR_WANT_READ:
{
- if(!readSelect(timeout))
- {
- // Timeout and wait for them to arrive.
- throw TimeoutException(__FILE__, __LINE__);
- }
+ readSelect(timeout);
continue;
}
- case SSL_ERROR_WANT_WRITE:
- case SSL_ERROR_WANT_X509_LOOKUP:
- {
- // Perform another read. The read should take care of this.
- continue;
- }
+ //
+ // If session renegotiation is ever enabled this could
+ // occur.
+ //
+ //case SSL_ERROR_WANT_WRITE:
+ //{
+ // writeSelect(timeout);
+ // break;
+ //}
case SSL_ERROR_SYSCALL:
{
- if(bytesRead == -1)
+ if(ret == -1)
{
// IO Error in underlying BIO
if(interrupted())
{
- break;
+ continue;
}
+ if(noBuffers() && packetSize > 1024)
+ {
+ packetSize /= 2;
+ continue;
+ }
+
+ //
+ // Its not clear whether this can occur, isn't
+ // this the same as SSL_ERROR_WANT_READ?
+ //
if(wouldBlock())
{
- break;
+ readSelect(timeout);
+ continue;
}
- if(connectionLost())
+ if(!connectionLost())
{
- //
- // If the connection is lost when reading data, we shut
- // down the write end of the socket. This helps to unblock
- // threads that are stuck in send() or select() while
- // sending data. Note: I don't really understand why
- // send() or select() sometimes don't detect a connection
- // loss. Therefore this helper to make them detect it.
- //
- //assert(_fd != INVALID_SOCKET);
- //shutdownSocket(_fd);
-
- ConnectionLostException ex(__FILE__, __LINE__);
- ex.error = getSocketErrno();
- throw ex;
- }
-
- SocketException ex(__FILE__, __LINE__);
- ex.error = getSocketErrno();
- throw ex;
- }
- else // (bytesRead == 0)
- {
- //
- // See the commment above about shutting down the
- // socket if the connection is lost while reading
- // data.
- //
- //assert(_fd != INVALID_SOCKET);
- //shutdownSocket(_fd);
-
- ConnectionLostException ex(__FILE__, __LINE__);
- ex.error = 0;
- throw ex;
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
}
- }
-
- case SSL_ERROR_SSL:
- {
- ProtocolException protocolEx(__FILE__, __LINE__);
-
- protocolEx.message = "encountered a violation of the ssl protocol\n";
- protocolEx.message += sslGetErrors();
-
- throw protocolEx;
- }
+ // fall throught
+ }
case SSL_ERROR_ZERO_RETURN:
- {
+ {
+
//
- // See the commment above about shutting down the
- // socket if the connection is lost while reading
- // data.
+ // If the connection is lost when reading data, we shut
+ // down the write end of the socket. This helps to unblock
+ // threads that are stuck in send() or select() while
+ // sending data. Note: I don't really understand why
+ // send() or select() sometimes don't detect a connection
+ // loss. Therefore this helper to make them detect it.
//
//assert(_fd != INVALID_SOCKET);
//shutdownSocket(_fd);
- // Indicates that that the SSL Connection has been closed.
- // But does not necessarily indicate that the underlying transport
- // has been closed (in the case of Ice, it definitely hasn't yet).
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
- ConnectionLostException ex(__FILE__, __LINE__);
- ex.error = getSocketErrno();
+ case SSL_ERROR_SSL:
+ {
+ ProtocolException ex(__FILE__, __LINE__);
+ ex.message = "encountered a violation of the ssl protocol\n";
+ ex.message += sslGetErrors();
throw ex;
}
- }
- }
- if(totalBytesRead == 0)
- {
- if(_traceLevels->security >= IceSSL::SECURITY_WARNINGS)
- {
- Trace out(_logger, _traceLevels->securityCat);
- out << "WRN reading from ssl connection returns no bytes";
+ default:
+ {
+ Warning warn(_logger);
+ warn << "IceSSL: unexpected result from SSL_write: " << SSL_get_error(_sslConnection, ret);
+ }
}
+
+ if(_traceLevels->network >= 3)
+ {
+ Trace out(_logger, _traceLevels->networkCat);
+ out << "received " << ret << " of " << packetSize; out << " bytes via ssl\n" << toString();
+ }
+
+ if(_stats)
+ {
+ _stats->bytesReceived(type(), ret);
+ }
+
+ buf.i += ret;
+
+ if(packetSize > buf.b.end() - buf.i)
+ {
+ packetSize = static_cast<Buffer::Container::difference_type>(buf.b.end() - buf.i);
+ }
}
}
@@ -345,48 +386,163 @@ IceSSL::SslTransceiver::toString() const
}
void
-IceSSL::SslTransceiver::forceHandshake()
+IceSSL::SslTransceiver::initialize(int timeout)
{
- try
- {
- if(handshake(_readTimeout) > 0)
- {
- return; // Handshake complete.
- }
- }
- catch(const TimeoutException&)
+ assert(_sslConnection != 0);
+
+ if(_traceLevels->security >= IceSSL::SECURITY_PROTOCOL)
{
- // Fall through.
+ Trace out(_logger, _traceLevels->securityCat);
+ out << "Performing handshake.\n";
+ out << fdToString(SSL_get_fd(_sslConnection));
}
- if(_traceLevels->security >= IceSSL::SECURITY_WARNINGS)
+ while(true)
{
- Trace out(_logger, _traceLevels->securityCat);
- if(_readTimeout >= 0)
+ ERR_clear_error();
+ int result;
+ if(_contextType == IceSSL::Client)
{
- out << "Timeout occurred during SSL handshake.\n" << toString();
+ result = SSL_connect(_sslConnection);
}
else
{
- out << "Failure occurred during SSL handshake.\n" << toString();
+ result = SSL_accept(_sslConnection);
}
- }
-
- close();
- if(_readTimeout >= 0)
- {
- throw ConnectTimeoutException(__FILE__, __LINE__);
- }
- else
- {
- ConnectionRefusedException ex(__FILE__, __LINE__);
-#ifdef _WIN32
- ex.error = WSAECONNREFUSED;
-#else
- ex.error = ECONNREFUSED;
+ //
+ // Success?
+ //
+ if(result == 1)
+ {
+ assert(SSL_is_init_finished(_sslConnection));
+
+ //
+ // Init finished, look at the connection information.
+ //
+ if((_traceLevels->security >= IceSSL::SECURITY_PROTOCOL_DEBUG) && 0)
+ {
+#ifdef ICE_SSL_EXTRA_TRACING
+ //
+ // Only in extreme cases do we enable this, partially because it doesn't use the Logger.
+ //
+ BIOJanitor bioJanitor(BIO_new_fp(stdout, BIO_NOCLOSE));
+ BIO* bio = bioJanitor.get();
+
+ showCertificateChain(bio);
+
+ showPeerCertificate(bio, _contextType == IceSSL::Server : "Server" ? "Client");
+
+ showSharedCiphers(bio);
+
+ showSelectedCipherInfo(bio);
+
+ showHandshakeStats(bio);
+
+ showSessionInfo(bio);
#endif
- throw ex;
+ }
+ return;
+ }
+
+ switch(SSL_get_error(_sslConnection, result))
+ {
+ case SSL_ERROR_WANT_READ:
+ {
+ readSelect(timeout);
+ continue;
+ }
+
+ case SSL_ERROR_WANT_WRITE:
+ {
+ writeSelect(timeout);
+ continue;
+ }
+
+ case SSL_ERROR_NONE:
+ {
+ continue;
+ }
+
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ {
+ Warning warn(_logger);
+ warn << "SSL_ERROR_NONE";
+ continue;
+ }
+
+ case SSL_ERROR_SYSCALL:
+ {
+ if(result == -1)
+ {
+ if(interrupted())
+ {
+ break;
+ }
+
+ assert(!wouldBlock());
+
+ if(connectionLost())
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ }
+
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+
+ case SSL_ERROR_SSL:
+ {
+ long err = SSL_get_verify_result(_sslConnection);
+ if(err != X509_V_OK)
+ {
+ //
+ // On a client we raise the CertificateVerificationException. On the
+ // server side we simply close the connection.
+ //
+ if(_contextType == IceSSL::Client)
+ {
+ CertificateVerificationException ex(__FILE__, __LINE__);
+ ex.message = getVerificationError(err);
+ string errors = sslGetErrors();
+ if(!errors.empty())
+ {
+ ex.message += "\n";
+ ex.message += errors;
+ }
+ throw ex;
+ }
+ else
+ {
+ //
+ // Validation failed, close the connection.
+ //
+ throw ConnectionRefusedException(__FILE__, __LINE__);
+ }
+ }
+
+ //
+ // This happens if the client or server silently drop
+ // the connection. That can occur if the server
+ // doesn't trust the client for example.
+ //
+ ProtocolException ex(__FILE__, __LINE__);
+ ex.message = "encountered a violation of the ssl protocol during handshake\n";
+ ex.message += sslGetErrors();
+ throw ex;
+ }
+
+ case SSL_ERROR_ZERO_RETURN:
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ }
}
}
@@ -405,10 +561,11 @@ IceSSL::SslTransceiver::getTransceiver(SSL* sslPtr)
}
//
-// Note: Do not throw exceptions from verifyCertificate - it would rip through the OpenSSL system,
-// interfering with the usual handling and alert system of the handshake. Exceptions should
-// be caught here (if they can be generated), logged and then a fail return code (0) should
-// returned.
+// Note: Do not throw exceptions from verifyCertificate - it would rip
+// through the OpenSSL system, interfering with the usual handling and
+// alert system of the handshake. Exceptions should be caught here
+// (if they can be generated), logged and then a fail return code (0)
+// should returned.
//
int
IceSSL::SslTransceiver::verifyCertificate(int preVerifyOkay, X509_STORE_CTX* x509StoreContext)
@@ -468,305 +625,112 @@ IceSSL::SslTransceiver::verifyCertificate(int preVerifyOkay, X509_STORE_CTX* x50
// Protected Methods
//
-// Note: I would use a using directive of the form:
-// using IceSSL::CertificateVerifierPtr;
-// but unfortunately, it appears that this is not properly picked up.
-//
-
-int
+void
IceSSL::SslTransceiver::internalShutdownWrite(int timeout)
{
- if(_sslConnection == 0)
- {
- return 1;
- }
-
- int retCode = 0;
-
- if(_initWantWrite)
- {
- int i = writeSelect(timeout);
-
- if(i == 0)
- {
- return 0;
- }
-
- _initWantWrite = 0;
- }
- else if(_initWantRead)
- {
- int i = readSelect(timeout);
-
- if(i == 0)
- {
- return 0;
- }
-
- _initWantRead = 0;
- }
-
- ERR_clear_error();
-
- retCode = SSL_shutdown(_sslConnection);
-
- if(retCode == 1)
- {
- // Shutdown successful - shut down the socket for writing.
- shutdownSocketWrite(SSL_get_fd(_sslConnection));
- }
- else if(retCode == -1)
- {
- setLastError(retCode);
-
- // Shutdown failed due to an error.
-
- switch(getLastError())
- {
- case SSL_ERROR_WANT_WRITE:
- {
- _initWantWrite = 1;
- retCode = 0;
- break;
- }
-
- case SSL_ERROR_WANT_READ:
- {
- _initWantRead = 1;
- retCode = 0;
- break;
- }
-
- case SSL_ERROR_NONE:
- case SSL_ERROR_WANT_X509_LOOKUP:
- {
- // Ignore
- retCode = 0;
- break;
- }
-
- case SSL_ERROR_SYSCALL:
- {
- //
- // Some error with the underlying transport.
- //
-
- if(interrupted())
- {
- retCode = 0;
- break;
- }
-
- if(wouldBlock())
- {
- readSelect(timeout);
- retCode = 0;
- break;
- }
-
- if(connectionLost())
- {
- ConnectionLostException ex(__FILE__, __LINE__);
- ex.error = getSocketErrno();
- throw ex;
- }
-
- //
- // Non-specific socket problem.
- //
- SocketException ex(__FILE__, __LINE__);
- ex.error = getSocketErrno();
- throw ex;
- }
-
- case SSL_ERROR_SSL:
- {
- //
- // Error in the SSL library, usually a Protocol error.
- //
-
- ProtocolException protocolEx(__FILE__, __LINE__);
-
- protocolEx.message = "encountered a violation of the ssl protocol during shutdown\n";
- protocolEx.message += sslGetErrors();
-
- throw protocolEx;
- }
-
- case SSL_ERROR_ZERO_RETURN:
- {
- //
- // Indicates that the SSL connection has been closed. For SSLv3.0
- // and TLSv1.0, it indicates that a closure alert was received,
- // and thus the connection has been closed cleanly.
- //
-
- CloseConnectionException ex(__FILE__, __LINE__);
- throw ex;
- }
- }
- }
-
- return retCode;
-}
-
-int
-IceSSL::SslTransceiver::connect()
-{
- assert(_sslConnection != 0);
-
- ERR_clear_error();
- int result = SSL_connect(_sslConnection);
-
- setLastError(result);
-
- return result;
-}
-
-int
-IceSSL::SslTransceiver::accept()
-{
- assert(_sslConnection != 0);
-
- ERR_clear_error();
- int result = SSL_accept(_sslConnection);
-
- setLastError(result);
-
- return result;
-}
-
-// NOTE: Currently not used, maybe later.
-int
-IceSSL::SslTransceiver::renegotiate()
-{
- assert(_sslConnection != 0);
- return SSL_renegotiate(_sslConnection);
-}
-
-int
-IceSSL::SslTransceiver::initialize(int timeout)
-{
- int retCode = 0;
-
while(true)
{
- // One lucky thread will get the honor of carrying out the hanshake,
- // if there is one to perform. The HandshakeSentinel effectively
- // establishes a first-come, first-serve policy. One thread will own
- // the handshake, and the others will either return rejected to the
- // caller (who will figure out what to do with them) OR wait until
- // our lead thread is done. Then, the shuffle begins again.
- // Eventually, all threads will filter through.
-
- HandshakeSentinel handshakeSentinel(_handshakeFlag);
-
- if(!handshakeSentinel.ownHandshake())
- {
- if(timeout >= 0)
- {
- // We should return immediately here - do not block,
- // leave it to the caller to figure this out.
- retCode = -1;
- break;
- }
- else
- {
- // We will wait here - blocking IO is being used.
- IceUtil::Mutex::Lock sync(_handshakeWaitMutex);
- }
- }
- else
- {
- // Perform our init(), then leave.
- IceUtil::Mutex::Lock sync(_handshakeWaitMutex);
-
- // Here we 'take the ball and run with it' for as long as we can
- // get away with it. As long as we don't encounter some error
- // status (or completion), this thread continues to service the
- // initialize() call.
- while(retCode == 0)
- {
- switch(_phase)
- {
- case Handshake :
- {
- retCode = handshake(timeout);
- break;
- }
-
- case Shutdown :
- {
- retCode = internalShutdownWrite(timeout);
- break;
- }
-
- case Connected :
- {
- retCode = SSL_is_init_finished(_sslConnection);
-
- if(!retCode)
- {
- // In this case, we are essentially renegotiating
- // the connection at the behest of the peer.
- _phase = Handshake;
- continue;
- }
-
- // Done here.
- return retCode;
- }
- }
- }
-
- break;
- }
- }
-
- return retCode;
-}
-
-int
-IceSSL::SslTransceiver::pending()
-{
- assert(_sslConnection != 0);
- return SSL_pending(_sslConnection);
-}
-
-int
-IceSSL::SslTransceiver::getLastError() const
-{
- assert(_sslConnection != 0);
- return SSL_get_error(_sslConnection, _lastError);
-}
-
-int
-IceSSL::SslTransceiver::sslRead(unsigned char* buffer, int bufferSize)
-{
- assert(_sslConnection != 0);
-
- ERR_clear_error();
- int bytesRead = SSL_read(_sslConnection, buffer, bufferSize);
+ ERR_clear_error();
+ int result = SSL_shutdown(_sslConnection);
+ if(result == 0)
+ {
+ //
+ // From the documentation:
+ //
+ // The shutdown is not yet finished. Call SSL_shutdown()
+ // for a second time, if a bidirectional shutdown shall be
+ // performed. The output of SSL_get_error(3) may be
+ // misleading, as an erroneous SSL_ERROR_SYSCALL may be
+ // flagged even though no error occurred.
+ //
+ // Call it one more time. If the result is 0 then we're done.
+ //
+ result = SSL_shutdown(_sslConnection);
+ if(result == 0)
+ {
+ return;
+ }
+ }
+ if(result == 1)
+ {
+ // Shutdown successful - shut down the socket for writing.
+ shutdownSocketWrite(SSL_get_fd(_sslConnection));
+ return;
+ }
+ else if(result == -1)
+ {
+ switch(SSL_get_error(_sslConnection, result))
+ {
+ case SSL_ERROR_WANT_WRITE:
+ {
+ writeSelect(timeout);
+ continue;
+ }
+
+ case SSL_ERROR_WANT_READ:
+ {
+ readSelect(timeout);
+ continue;
+ }
+
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ {
+ continue;
+ }
+
+ case SSL_ERROR_SYSCALL:
+ {
+ if(interrupted())
+ {
+ continue;
+ }
- setLastError(bytesRead);
+ assert(!wouldBlock());
- return bytesRead;
-}
+ if(connectionLost())
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
-int
-IceSSL::SslTransceiver::sslWrite(unsigned char* buffer, int bufferSize)
-{
- assert(_sslConnection != 0);
+ //
+ // Non-specific socket problem.
+ //
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
- ERR_clear_error();
- int bytesWritten = SSL_write(_sslConnection, buffer, bufferSize);
+ case SSL_ERROR_SSL:
+ {
+ //
+ // Error in the SSL library, usually a Protocol error.
+ //
- setLastError(bytesWritten);
+ ProtocolException ex(__FILE__, __LINE__);
+ ex.message = "encountered a violation of the ssl protocol during shutdown\n";
+ ex.message += sslGetErrors();
+ throw ex;
+ }
- return bytesWritten;
+ case SSL_ERROR_ZERO_RETURN:
+ {
+ //
+ // Indicates that the SSL connection has been
+ // closed. For SSLv3.0 and TLSv1.0, it indicates
+ // that a closure alert was received, and thus the
+ // connection has been closed cleanly.
+ //
+ throw CloseConnectionException(__FILE__, __LINE__);
+ }
+ }
+ }
+ }
}
-int
+void
IceSSL::SslTransceiver::select(int timeout, bool write)
{
int ret;
@@ -824,20 +788,18 @@ IceSSL::SslTransceiver::select(int timeout, bool write)
{
throw TimeoutException(__FILE__, __LINE__);
}
-
- return FD_ISSET(fd, &rwFdSet);
}
-int
+void
IceSSL::SslTransceiver::readSelect(int timeout)
{
- return select(timeout, false);
+ select(timeout, false);
}
-int
+void
IceSSL::SslTransceiver::writeSelect(int timeout)
{
- return select(timeout, true);
+ select(timeout, true);
}
//
@@ -1044,34 +1006,34 @@ IceSSL::SslTransceiver::showClientCAList(BIO* bio, const char* connType)
// Private Methods
//
-IceSSL::SslTransceiver::SslTransceiver(const OpenSSLPluginIPtr& plugin,
+IceSSL::SslTransceiver::SslTransceiver(const ContextType contextType,
+ const OpenSSLPluginIPtr& plugin,
SOCKET fd,
const CertificateVerifierPtr& certificateVerifier,
SSL* sslConnection,
int timeout) :
- _sslConnection(sslConnection),
- _readTimeout(timeout),
+ _contextType(contextType),
_plugin(plugin),
- _traceLevels(plugin->getTraceLevels()),
- _logger(plugin->getLogger()),
- _stats(plugin->getStats()),
_fd(fd),
- _certificateVerifier(certificateVerifier)
+ _certificateVerifier(certificateVerifier),
+ _sslConnection(sslConnection),
+ _logger(plugin->getLogger()),
+ _traceLevels(plugin->getTraceLevels()),
+ _stats(plugin->getStats())
{
assert(sslConnection != 0);
- FD_ZERO(&_rFdSet);
- FD_ZERO(&_wFdSet);
-
SSL_set_ex_data(sslConnection, 0, static_cast<void*>(plugin.get()));
- // We always start off in a Handshake
- _phase = Handshake;
-
- _lastError = SSL_ERROR_NONE;
-
- _initWantRead = 0;
- _initWantWrite = 0;
+ // Set the Connect Connection state for this connection.
+ if(contextType == IceSSL::Client)
+ {
+ SSL_set_connect_state(_sslConnection);
+ }
+ else
+ {
+ SSL_set_accept_state(_sslConnection);
+ }
// Set up the SSL to be able to refer back to our connection object.
addTransceiver(_sslConnection, this);
@@ -1079,11 +1041,10 @@ IceSSL::SslTransceiver::SslTransceiver(const OpenSSLPluginIPtr& plugin,
IceSSL::SslTransceiver::~SslTransceiver()
{
- if(_sslConnection != 0)
- {
- removeTransceiver(_sslConnection);
- SSL_set_ex_data(_sslConnection, 0, 0);
- SSL_free(_sslConnection);
- _sslConnection = 0;
- }
+ assert(_sslConnection != 0);
+
+ removeTransceiver(_sslConnection);
+ SSL_set_ex_data(_sslConnection, 0, 0);
+ SSL_free(_sslConnection);
+ _sslConnection = 0;
}