diff options
Diffstat (limited to 'cpp/src/Ice/SslConnectionOpenSSL.cpp')
-rw-r--r-- | cpp/src/Ice/SslConnectionOpenSSL.cpp | 1376 |
1 files changed, 688 insertions, 688 deletions
diff --git a/cpp/src/Ice/SslConnectionOpenSSL.cpp b/cpp/src/Ice/SslConnectionOpenSSL.cpp index 231ef3a56a1..ae670b5063d 100644 --- a/cpp/src/Ice/SslConnectionOpenSSL.cpp +++ b/cpp/src/Ice/SslConnectionOpenSSL.cpp @@ -1,688 +1,688 @@ -// **********************************************************************
-//
-// Copyright (c) 2001
-// MutableRealms, Inc.
-// Huntsville, AL, USA
-//
-// All Rights Reserved
-//
-// **********************************************************************
-#ifdef WIN32
-#pragma warning(disable:4786)
-#endif
-
-#include <string>
-#include <sstream>
-#include <Ice/Network.h>
-#include <JTC/JTC.h>
-#include <Ice/Security.h>
-#include <Ice/SslException.h>
-#include <Ice/SslConnection.h>
-#include <Ice/SslSystemOpenSSL.h>
-
-using namespace std;
-using namespace IceInternal;
-
-using std::endl;
-
-////////////////////////////////
-////////// Connection //////////
-////////////////////////////////
-
-//
-// Public Methods
-//
-
-IceSecurity::Ssl::OpenSSL::Connection::Connection(SSL* sslConnection, string& systemID)
-{
- if (sslConnection == 0)
- {
- string errorString = "Construction of Connection with NULL SSL pointer.";
-
- throw ContextException(errorString.c_str(), __FILE__, __LINE__);
- }
-
- // Get the system we were generated from
- _system = IceSecurity::Ssl::Factory::getSystem(systemID);
-
- _sslConnection = sslConnection;
-
- _lastError = SSL_ERROR_NONE;
-
- initWantRead = 0;
- initWantWrite = 0;
-}
-
-IceSecurity::Ssl::OpenSSL::Connection::~Connection()
-{
- METHOD_INV("OpenSSL::Connection::~Connection()");
-
- shutdown();
-
- IceSecurity::Ssl::Factory::releaseSystem(_system);
-
- METHOD_RET("OpenSSL::Connection::~Connection()");
-}
-
-void
-IceSecurity::Ssl::OpenSSL::Connection::shutdown()
-{
- METHOD_INV("OpenSSL::Connection::shutdown()");
-
- if (_sslConnection != 0)
- {
- WARNING(string("shutting down SSL connection\n") + fdToString(SSL_get_fd(_sslConnection)));
-
- SSL_free(_sslConnection);
- _sslConnection = 0;
- }
-
- METHOD_RET("OpenSSL::Connection::shutdown()");
-}
-
-//
-// Protected Methods
-//
-
-int
-IceSecurity::Ssl::OpenSSL::Connection::connect()
-{
- METHOD_INV("OpenSSL::Connection::connect()");
-
- int result = SSL_connect(_sslConnection);
-
- setLastError(result);
-
- METHOD_RET("OpenSSL::Connection::connect()");
-
- return result;
-}
-
-int
-IceSecurity::Ssl::OpenSSL::Connection::accept()
-{
- METHOD_INV("OpenSSL::Connection::accept()");
-
- int result = SSL_accept(_sslConnection);
-
- setLastError(result);
-
- METHOD_RET("OpenSSL::Connection::accept()");
-
- return result;
-}
-
-int
-IceSecurity::Ssl::OpenSSL::Connection::renegotiate()
-{
- METHOD_INS("OpenSSL::Connection::renegotiate()");
-
- return SSL_renegotiate(_sslConnection);
-}
-
-int
-IceSecurity::Ssl::OpenSSL::Connection::sslRead(char* buffer, int bufferSize)
-{
- METHOD_INV("OpenSSL::Connection::sslRead()");
-
- int bytesRead = SSL_read(_sslConnection, buffer, bufferSize);
-
- setLastError(bytesRead);
-
- METHOD_RET("OpenSSL::Connection::sslRead()");
-
- return bytesRead;
-}
-
-int
-IceSecurity::Ssl::OpenSSL::Connection::sslWrite(char* buffer, int bufferSize)
-{
- METHOD_INV("OpenSSL::Connection::sslWrite()");
-
- int bytesWritten = SSL_write(_sslConnection, buffer, bufferSize);
-
- setLastError(bytesWritten);
-
- METHOD_RET("OpenSSL::Connection::sslWrite()");
-
- return bytesWritten;
-}
-
-
-void
-IceSecurity::Ssl::OpenSSL::Connection::printGetError(int errCode)
-{
- if (SECURITY_LEVEL_PROTOCOL_DEBUG)
- {
- string errorString;
-
- switch (errCode)
- {
- case SSL_ERROR_NONE :
- {
- errorString = "SSL_ERROR_NONE";
- break;
- }
-
- case SSL_ERROR_ZERO_RETURN :
- {
- errorString = "SSL_ERROR_ZERO_RETURN";
- break;
- }
-
- case SSL_ERROR_WANT_READ :
- {
- errorString = "SSL_ERROR_WANT_READ";
- break;
- }
-
- case SSL_ERROR_WANT_WRITE :
- {
- errorString = "SSL_ERROR_WANT_WRITE";
- break;
- }
-
- case SSL_ERROR_WANT_CONNECT :
- {
- errorString = "SSL_ERROR_WANT_CONNECT";
- break;
- }
-
- case SSL_ERROR_WANT_X509_LOOKUP :
- {
- errorString = "SSL_ERROR_WANT_X509_LOOKUP";
- break;
- }
-
- case SSL_ERROR_SYSCALL :
- {
- errorString = "SSL_ERROR_SYSCALL";
- break;
- }
-
- case SSL_ERROR_SSL :
- {
- errorString = "SSL_ERROR_SSL";
- break;
- }
- }
-
- if (!errorString.empty())
- {
- SECURITY_LOGGER(string("Encountered: ") + errorString)
- }
- }
-}
-
-// protocolWrite()
-//
-// The entire purpose of this strange little routine is to provide OpenSSL with a
-// SSL_write() when they request one (this is for handshaking purposes). It writes
-// nothing at all. Its entire purpose is jut to call the SSL_write() through one.
-// of our defined methods. The SSL_write() will end up only writing protocol handshake
-// packets, not application packets. This looks wierd, but it is essentially what
-// the demo programs are doing, so I feel okay copying them. The only reason that I
-// have defined the buffer[] array is so that I have a valid buffer pointer.
-void
-IceSecurity::Ssl::OpenSSL::Connection::protocolWrite()
-{
- METHOD_INV("OpenSSL::Connection::protocolWrite()");
-
- static char buffer[10];
-
- memset(buffer, 0, sizeof(buffer));
-
- // Note: We should be calling the write(char*,int) method here,
- // not the write(Buffer&,int) method. If things start acting
- // strangely, check this!
- sslWrite(buffer,0);
-
- METHOD_RET("OpenSSL::Connection::protocolWrite()");
-}
-
-int
-IceSecurity::Ssl::OpenSSL::Connection::readInBuffer(Buffer& buf)
-{
- JTCSyncT<JTCMutex> sync(_inBufferMutex);
-
- METHOD_INV("OpenSSL::Connection::readInBuffer()");
-
- int bytesRead = 0;
-
- if (!_inBuffer.b.empty())
- {
- // Just how big is the destination?
- int bufferSize = buf.b.end() - buf.i;
-
- // And how much do we have in our _inBuffer to copy?
- int inBufferSize = _inBuffer.i - _inBuffer.b.begin();
-
- // Select how many bytes we can handle.
- bytesRead = min(bufferSize, inBufferSize);
-
- // Iterators that indicate how much of the _inBuffer we're going to copy
- Buffer::Container::iterator inBufferBegin = _inBuffer.b.begin();
- Buffer::Container::iterator inBufferEndAt = (_inBuffer.b.begin() + bytesRead);
-
- // Copy over the bytes from the _inBuffer to our destination buffer
- buf.i = copy(inBufferBegin, inBufferEndAt, buf.i);
-
- // Erase the data that we've copied out of the _inBuffer.
- _inBuffer.b.erase(inBufferBegin, inBufferEndAt);
-
- if (SECURITY_LEVEL_PROTOCOL)
- {
- string protocolString = "Copied ";
- protocolString += Int(bytesRead);
- protocolString += string(" bytes from SSL buffer\n");
- protocolString += fdToString(SSL_get_fd(_sslConnection));
-
- PROTOCOL(protocolString);
- }
- }
-
- METHOD_RET("OpenSSL::Connection::readInBuffer()");
-
- return bytesRead;
-}
-
-int
-IceSecurity::Ssl::OpenSSL::Connection::readSelect(int timeout)
-{
- METHOD_INV("OpenSSL::Connection::readSelect()");
-
- int ret;
- int fd = SSL_get_fd(_sslConnection);
- fd_set rFdSet;
-
- struct timeval tv;
-
- if (timeout >= 0)
- {
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000;
- }
-
- do
- {
- FD_ZERO(&rFdSet);
- FD_SET(fd, &rFdSet);
-
- if (timeout >= 0)
- {
- ret = ::select(fd + 1, &rFdSet, 0, 0, &tv);
- }
- else
- {
- ret = ::select(fd + 1, &rFdSet, 0, 0, 0);
- }
- }
- while (ret == SOCKET_ERROR && interrupted());
-
- if (ret == SOCKET_ERROR)
- {
- SocketException ex(__FILE__, __LINE__);
- ex.error = getSocketErrno();
- throw ex;
- }
-
- if (ret == 0)
- {
- throw TimeoutException(__FILE__, __LINE__);
- }
-
- METHOD_RET("OpenSSL::Connection::readSelect()");
-
- return FD_ISSET(fd, &rFdSet);
-}
-
-int
-IceSecurity::Ssl::OpenSSL::Connection::writeSelect(int timeout)
-{
- METHOD_INV("OpenSSL::Connection::writeSelect()");
-
- int ret;
- int fd = SSL_get_fd(_sslConnection);
- fd_set wFdSet;
-
- struct timeval tv;
-
- if (timeout >= 0)
- {
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000;
- }
-
- do
- {
- FD_ZERO(&wFdSet);
- FD_SET(fd, &wFdSet);
-
- if (timeout >= 0)
- {
- ret = ::select(fd + 1, 0, &wFdSet, 0, &tv);
- }
- else
- {
- ret = ::select(fd + 1, 0, &wFdSet, 0, 0);
- }
- }
- while (ret == SOCKET_ERROR && interrupted());
-
- if (ret == SOCKET_ERROR)
- {
- SocketException ex(__FILE__, __LINE__);
- ex.error = getSocketErrno();
- throw ex;
- }
-
- if (ret == 0)
- {
- throw TimeoutException(__FILE__, __LINE__);
- }
-
- METHOD_RET("OpenSSL::Connection::writeSelect()");
-
- return FD_ISSET(fd, &wFdSet);
-}
-
-int
-IceSecurity::Ssl::OpenSSL::Connection::readSSL(Buffer& buf, int timeout)
-{
- METHOD_INV("OpenSSL::Connection::readSSL()");
-
- int packetSize = buf.b.end() - buf.i;
- int totalBytesRead = 0;
- int bytesPending;
- int bytesRead;
-
- // We keep reading until we're done.
- while (buf.i != buf.b.end())
- {
- // Ensure we're initialized.
- if (init(timeout))
- {
- bytesPending = pending();
-
- if (!bytesPending && readSelect(timeout))
- {
- bytesPending = 1;
- }
-
- if (!bytesPending)
- {
- // We're done here.
- break;
- }
-
- bytesRead = sslRead((char *)buf.i, packetSize);
-
- switch (getLastError())
- {
- case SSL_ERROR_NONE:
- {
- if (bytesRead > 0)
- {
- ostringstream s;
-
- s << "received " << bytesRead << " of " << packetSize;
- s << " bytes via SSL\n" << fdToString(SSL_get_fd(_sslConnection));
-
- PROTOCOL(s.str());
-
- totalBytesRead += bytesRead;
-
- buf.i += bytesRead;
-
- if (packetSize > buf.b.end() - buf.i)
- {
- packetSize = buf.b.end() - buf.i;
- }
- }
- else
- {
- // TODO: The client application performs a cleanup at this point,
- // not even shutting down SSL - it just frees the SSL
- // structure. The server does nothing. I'm ignoring this,
- // at the moment, I'm sure it will come back at me.
-
- PROTOCOL("Error SSL_ERROR_NONE: Repeating as per protocol.");
- }
- continue;
- }
-
- case SSL_ERROR_WANT_WRITE:
- {
- // If we get this error here, it HAS to be because the protocol wants
- // to do something handshake related. As such, We're going to call
- // write with an empty buffer. I've seen this done in the demo
- // programs, so this should be valid. No actual application data
- // will be sent, just protocol packets.
-
- PROTOCOL("Error SSL_ERROR_WANT_WRITE.");
-
- protocolWrite();
-
- continue;
- }
-
- case SSL_ERROR_WANT_READ:
- {
- // Repeat with the same arguments! (as in the OpenSSL documentation)
- // Whatever happened, the last read didn't actually read anything for
- // us. This is effectively a retry.
-
- PROTOCOL("Error SSL_ERROR_WANT_READ: Repeating as per protocol.");
-
- continue;
- }
-
- case SSL_ERROR_WANT_X509_LOOKUP:
- {
- // Perform another read. The read should take care of this.
-
- PROTOCOL("Error SSL_ERROR_WANT_X509_LOOKUP: Repeating as per protocol.");
-
- continue;
- }
-
- case SSL_ERROR_SYSCALL:
- {
- if(bytesRead == -1)
- {
- SocketException ex(__FILE__, __LINE__);
- ex.error = getSocketErrno();
- throw ex;
- }
- else
- {
- string errorString = "SSL_ERROR_SYSCALL";
-
- EXCEPTION(errorString);
-
- throw ShutdownException(errorString.c_str(), __FILE__, __LINE__);
- }
- }
-
- case SSL_ERROR_SSL:
- {
- string errorString = "SSL_ERROR_SSL";
-
- EXCEPTION(errorString);
-
- throw ShutdownException(errorString.c_str(), __FILE__, __LINE__);
- }
-
- case SSL_ERROR_ZERO_RETURN:
- {
- string errorString = "SSL_ERROR_ZERO_RETURN";
-
- EXCEPTION(errorString);
-
- throw ShutdownException(errorString.c_str(), __FILE__, __LINE__);
- }
- }
- }
- }
-
- METHOD_RET("OpenSSL::Connection::readSSL()");
-
- return totalBytesRead;
-}
-
-void
-IceSecurity::Ssl::OpenSSL::Connection::showCertificateChain(BIO* bio)
-{
- STACK_OF(X509)* sk;
-
- // Big nasty buffer
- char buffer[4096];
-
- if ((sk = SSL_get_peer_cert_chain(_sslConnection)) != 0)
- {
- BIO_printf(bio,"---\nCertificate chain\n");
-
- for (int i = 0; i < sk_X509_num(sk); i++)
- {
- X509_NAME_oneline(X509_get_subject_name(sk_X509_value(sk,i)), buffer, sizeof(buffer));
- BIO_printf(bio, "%2d s:%s\n", i, buffer);
-
- X509_NAME_oneline(X509_get_issuer_name(sk_X509_value(sk,i)), buffer, sizeof(buffer));
- BIO_printf(bio, " i:%s\n", buffer);
-
- PEM_write_bio_X509(bio, sk_X509_value(sk, i));
- }
- }
- else
- {
- BIO_printf(bio, "---\nNo peer certificate chain available.\n");
- }
-}
-
-void
-IceSecurity::Ssl::OpenSSL::Connection::showPeerCertificate(BIO* bio, const char* connType)
-{
- X509* peerCert = 0;
- char buffer[4096];
-
- if ((peerCert = SSL_get_peer_certificate(_sslConnection)) != 0)
- {
- BIO_printf(bio, "%s Certificate\n", connType);
- PEM_write_bio_X509(bio, peerCert);
-
- X509_NAME_oneline(X509_get_subject_name(peerCert), buffer, sizeof(buffer));
- BIO_printf(bio, "subject=%s\n", buffer);
-
- X509_NAME_oneline(X509_get_issuer_name(peerCert), buffer, sizeof(buffer));
- BIO_printf(bio, "issuer=%s\n", buffer);
-
- EVP_PKEY *pktmp;
- pktmp = X509_get_pubkey(peerCert);
- BIO_printf(bio,"%s public key is %d bit\n", connType, EVP_PKEY_bits(pktmp));
- EVP_PKEY_free(pktmp);
-
- X509_free(peerCert);
- }
- else
- {
- BIO_printf(bio, "No %s certificate available.\n", connType);
- }
-}
-
-void
-IceSecurity::Ssl::OpenSSL::Connection::showSharedCiphers(BIO* bio)
-{
- char buffer[4096];
- char* strpointer = 0;
-
- if ((strpointer = SSL_get_shared_ciphers(_sslConnection, buffer, sizeof(buffer))) != 0)
- {
- // This works only for SSL 2. In later protocol
- // versions, the client does not know what other
- // ciphers (in addition to the one to be used
- // in the current connection) the server supports.
-
- BIO_printf(bio, "---\nShared Ciphers:\n");
-
- int j = 0;
- int i = 0;
-
- while (*strpointer)
- {
- if (*strpointer == ':')
- {
- BIO_write(bio, " ", (15-j%25));
- i++;
- j=0;
- BIO_write(bio, ((i%3)?" ":"\n"), 1);
- }
- else
- {
- BIO_write(bio, strpointer, 1);
- j++;
- }
-
- strpointer++;
- }
-
- BIO_write(bio,"\n",1);
- }
-}
-
-void
-IceSecurity::Ssl::OpenSSL::Connection::showSessionInfo(BIO* bio)
-{
- if (_sslConnection->hit)
- {
- BIO_printf(bio, "Reused session-id\n");
- }
-
- PEM_write_bio_SSL_SESSION(bio, SSL_get_session(_sslConnection));
-}
-
-void
-IceSecurity::Ssl::OpenSSL::Connection::showSelectedCipherInfo(BIO* bio)
-{
- const char* str;
- SSL_CIPHER* cipher;
-
- // Show the cipher that was finally selected.
- cipher = SSL_get_current_cipher(_sslConnection);
-
- str = SSL_CIPHER_get_name(cipher);
- BIO_printf(bio, "Cipher Version: %s\n", ((str != 0) ? str : "(NONE)"));
-
- str = SSL_CIPHER_get_version(cipher);
- BIO_printf(bio, "Cipher Name: %s\n", ((str != 0) ? str : "(NONE)"));
-}
-
-void
-IceSecurity::Ssl::OpenSSL::Connection::showHandshakeStats(BIO* bio)
-{
- BIO_printf(bio, "---\nSSL handshake has read %ld bytes and written %ld bytes\n",
- BIO_number_read(SSL_get_rbio(_sslConnection)),
- BIO_number_written(SSL_get_wbio(_sslConnection)));
-}
-
-void
-IceSecurity::Ssl::OpenSSL::Connection::showClientCAList(BIO* bio, const char* connType)
-{
- char buffer[4096];
- STACK_OF(X509_NAME)* sk = SSL_get_client_CA_list(_sslConnection);
-
- if ((sk != 0) && (sk_X509_NAME_num(sk) > 0))
- {
- BIO_printf(bio,"---\nAcceptable %s certificate CA names\n", connType);
-
- for (int i = 0; i < sk_X509_NAME_num(sk); i++)
- {
- X509_NAME_oneline(sk_X509_NAME_value(sk, i), buffer, sizeof(buffer));
- BIO_write(bio, buffer, strlen(buffer));
- BIO_write(bio,"\n", 1);
- }
- }
- else
- {
- BIO_printf(bio,"---\nNo %s certificate CA names sent\n", connType);
- }
-}
+// ********************************************************************** +// +// Copyright (c) 2001 +// MutableRealms, Inc. +// Huntsville, AL, USA +// +// All Rights Reserved +// +// ********************************************************************** +#ifdef WIN32 +#pragma warning(disable:4786) +#endif + +#include <string> +#include <sstream> +#include <Ice/Network.h> +#include <JTC/JTC.h> +#include <Ice/Security.h> +#include <Ice/SslException.h> +#include <Ice/SslConnection.h> +#include <Ice/SslSystemOpenSSL.h> + +using namespace std; +using namespace IceInternal; + +using std::endl; + +//////////////////////////////// +////////// Connection ////////// +//////////////////////////////// + +// +// Public Methods +// + +IceSecurity::Ssl::OpenSSL::Connection::Connection(SSL* sslConnection, string& systemID) +{ + if (sslConnection == 0) + { + string errorString = "Construction of Connection with NULL SSL pointer."; + + throw ContextException(errorString.c_str(), __FILE__, __LINE__); + } + + // Get the system we were generated from + _system = IceSecurity::Ssl::Factory::getSystem(systemID); + + _sslConnection = sslConnection; + + _lastError = SSL_ERROR_NONE; + + initWantRead = 0; + initWantWrite = 0; +} + +IceSecurity::Ssl::OpenSSL::Connection::~Connection() +{ + METHOD_INV("OpenSSL::Connection::~Connection()"); + + shutdown(); + + IceSecurity::Ssl::Factory::releaseSystem(_system); + + METHOD_RET("OpenSSL::Connection::~Connection()"); +} + +void +IceSecurity::Ssl::OpenSSL::Connection::shutdown() +{ + METHOD_INV("OpenSSL::Connection::shutdown()"); + + if (_sslConnection != 0) + { + WARNING(string("shutting down SSL connection\n") + fdToString(SSL_get_fd(_sslConnection))); + + SSL_free(_sslConnection); + _sslConnection = 0; + } + + METHOD_RET("OpenSSL::Connection::shutdown()"); +} + +// +// Protected Methods +// + +int +IceSecurity::Ssl::OpenSSL::Connection::connect() +{ + METHOD_INV("OpenSSL::Connection::connect()"); + + int result = SSL_connect(_sslConnection); + + setLastError(result); + + METHOD_RET("OpenSSL::Connection::connect()"); + + return result; +} + +int +IceSecurity::Ssl::OpenSSL::Connection::accept() +{ + METHOD_INV("OpenSSL::Connection::accept()"); + + int result = SSL_accept(_sslConnection); + + setLastError(result); + + METHOD_RET("OpenSSL::Connection::accept()"); + + return result; +} + +int +IceSecurity::Ssl::OpenSSL::Connection::renegotiate() +{ + METHOD_INS("OpenSSL::Connection::renegotiate()"); + + return SSL_renegotiate(_sslConnection); +} + +int +IceSecurity::Ssl::OpenSSL::Connection::sslRead(char* buffer, int bufferSize) +{ + METHOD_INV("OpenSSL::Connection::sslRead()"); + + int bytesRead = SSL_read(_sslConnection, buffer, bufferSize); + + setLastError(bytesRead); + + METHOD_RET("OpenSSL::Connection::sslRead()"); + + return bytesRead; +} + +int +IceSecurity::Ssl::OpenSSL::Connection::sslWrite(char* buffer, int bufferSize) +{ + METHOD_INV("OpenSSL::Connection::sslWrite()"); + + int bytesWritten = SSL_write(_sslConnection, buffer, bufferSize); + + setLastError(bytesWritten); + + METHOD_RET("OpenSSL::Connection::sslWrite()"); + + return bytesWritten; +} + + +void +IceSecurity::Ssl::OpenSSL::Connection::printGetError(int errCode) +{ + if (SECURITY_LEVEL_PROTOCOL_DEBUG) + { + string errorString; + + switch (errCode) + { + case SSL_ERROR_NONE : + { + errorString = "SSL_ERROR_NONE"; + break; + } + + case SSL_ERROR_ZERO_RETURN : + { + errorString = "SSL_ERROR_ZERO_RETURN"; + break; + } + + case SSL_ERROR_WANT_READ : + { + errorString = "SSL_ERROR_WANT_READ"; + break; + } + + case SSL_ERROR_WANT_WRITE : + { + errorString = "SSL_ERROR_WANT_WRITE"; + break; + } + + case SSL_ERROR_WANT_CONNECT : + { + errorString = "SSL_ERROR_WANT_CONNECT"; + break; + } + + case SSL_ERROR_WANT_X509_LOOKUP : + { + errorString = "SSL_ERROR_WANT_X509_LOOKUP"; + break; + } + + case SSL_ERROR_SYSCALL : + { + errorString = "SSL_ERROR_SYSCALL"; + break; + } + + case SSL_ERROR_SSL : + { + errorString = "SSL_ERROR_SSL"; + break; + } + } + + if (!errorString.empty()) + { + SECURITY_LOGGER(string("Encountered: ") + errorString) + } + } +} + +// protocolWrite() +// +// The entire purpose of this strange little routine is to provide OpenSSL with a +// SSL_write() when they request one (this is for handshaking purposes). It writes +// nothing at all. Its entire purpose is jut to call the SSL_write() through one. +// of our defined methods. The SSL_write() will end up only writing protocol handshake +// packets, not application packets. This looks wierd, but it is essentially what +// the demo programs are doing, so I feel okay copying them. The only reason that I +// have defined the buffer[] array is so that I have a valid buffer pointer. +void +IceSecurity::Ssl::OpenSSL::Connection::protocolWrite() +{ + METHOD_INV("OpenSSL::Connection::protocolWrite()"); + + static char buffer[10]; + + memset(buffer, 0, sizeof(buffer)); + + // Note: We should be calling the write(char*,int) method here, + // not the write(Buffer&,int) method. If things start acting + // strangely, check this! + sslWrite(buffer,0); + + METHOD_RET("OpenSSL::Connection::protocolWrite()"); +} + +int +IceSecurity::Ssl::OpenSSL::Connection::readInBuffer(Buffer& buf) +{ + JTCSyncT<JTCMutex> sync(_inBufferMutex); + + METHOD_INV("OpenSSL::Connection::readInBuffer()"); + + int bytesRead = 0; + + if (!_inBuffer.b.empty()) + { + // Just how big is the destination? + int bufferSize = buf.b.end() - buf.i; + + // And how much do we have in our _inBuffer to copy? + int inBufferSize = _inBuffer.i - _inBuffer.b.begin(); + + // Select how many bytes we can handle. + bytesRead = min(bufferSize, inBufferSize); + + // Iterators that indicate how much of the _inBuffer we're going to copy + Buffer::Container::iterator inBufferBegin = _inBuffer.b.begin(); + Buffer::Container::iterator inBufferEndAt = (_inBuffer.b.begin() + bytesRead); + + // Copy over the bytes from the _inBuffer to our destination buffer + buf.i = copy(inBufferBegin, inBufferEndAt, buf.i); + + // Erase the data that we've copied out of the _inBuffer. + _inBuffer.b.erase(inBufferBegin, inBufferEndAt); + + if (SECURITY_LEVEL_PROTOCOL) + { + string protocolString = "Copied "; + protocolString += Int(bytesRead); + protocolString += string(" bytes from SSL buffer\n"); + protocolString += fdToString(SSL_get_fd(_sslConnection)); + + PROTOCOL(protocolString); + } + } + + METHOD_RET("OpenSSL::Connection::readInBuffer()"); + + return bytesRead; +} + +int +IceSecurity::Ssl::OpenSSL::Connection::readSelect(int timeout) +{ + METHOD_INV("OpenSSL::Connection::readSelect()"); + + int ret; + int fd = SSL_get_fd(_sslConnection); + fd_set rFdSet; + + struct timeval tv; + + if (timeout >= 0) + { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000; + } + + do + { + FD_ZERO(&rFdSet); + FD_SET(fd, &rFdSet); + + if (timeout >= 0) + { + ret = ::select(fd + 1, &rFdSet, 0, 0, &tv); + } + else + { + ret = ::select(fd + 1, &rFdSet, 0, 0, 0); + } + } + while (ret == SOCKET_ERROR && interrupted()); + + if (ret == SOCKET_ERROR) + { + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + if (ret == 0) + { + throw TimeoutException(__FILE__, __LINE__); + } + + METHOD_RET("OpenSSL::Connection::readSelect()"); + + return FD_ISSET(fd, &rFdSet); +} + +int +IceSecurity::Ssl::OpenSSL::Connection::writeSelect(int timeout) +{ + METHOD_INV("OpenSSL::Connection::writeSelect()"); + + int ret; + int fd = SSL_get_fd(_sslConnection); + fd_set wFdSet; + + struct timeval tv; + + if (timeout >= 0) + { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000; + } + + do + { + FD_ZERO(&wFdSet); + FD_SET(fd, &wFdSet); + + if (timeout >= 0) + { + ret = ::select(fd + 1, 0, &wFdSet, 0, &tv); + } + else + { + ret = ::select(fd + 1, 0, &wFdSet, 0, 0); + } + } + while (ret == SOCKET_ERROR && interrupted()); + + if (ret == SOCKET_ERROR) + { + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + + if (ret == 0) + { + throw TimeoutException(__FILE__, __LINE__); + } + + METHOD_RET("OpenSSL::Connection::writeSelect()"); + + return FD_ISSET(fd, &wFdSet); +} + +int +IceSecurity::Ssl::OpenSSL::Connection::readSSL(Buffer& buf, int timeout) +{ + METHOD_INV("OpenSSL::Connection::readSSL()"); + + int packetSize = buf.b.end() - buf.i; + int totalBytesRead = 0; + int bytesPending; + int bytesRead; + + // We keep reading until we're done. + while (buf.i != buf.b.end()) + { + // Ensure we're initialized. + if (init(timeout)) + { + bytesPending = pending(); + + if (!bytesPending && readSelect(timeout)) + { + bytesPending = 1; + } + + if (!bytesPending) + { + // We're done here. + break; + } + + bytesRead = sslRead((char *)buf.i, packetSize); + + switch (getLastError()) + { + case SSL_ERROR_NONE: + { + if (bytesRead > 0) + { + ostringstream s; + + s << "received " << bytesRead << " of " << packetSize; + s << " bytes via SSL\n" << fdToString(SSL_get_fd(_sslConnection)); + + PROTOCOL(s.str()); + + totalBytesRead += bytesRead; + + buf.i += bytesRead; + + if (packetSize > buf.b.end() - buf.i) + { + packetSize = buf.b.end() - buf.i; + } + } + else + { + // TODO: The client application performs a cleanup at this point, + // not even shutting down SSL - it just frees the SSL + // structure. The server does nothing. I'm ignoring this, + // at the moment, I'm sure it will come back at me. + + PROTOCOL("Error SSL_ERROR_NONE: Repeating as per protocol."); + } + continue; + } + + case SSL_ERROR_WANT_WRITE: + { + // If we get this error here, it HAS to be because the protocol wants + // to do something handshake related. As such, We're going to call + // write with an empty buffer. I've seen this done in the demo + // programs, so this should be valid. No actual application data + // will be sent, just protocol packets. + + PROTOCOL("Error SSL_ERROR_WANT_WRITE."); + + protocolWrite(); + + continue; + } + + case SSL_ERROR_WANT_READ: + { + // Repeat with the same arguments! (as in the OpenSSL documentation) + // Whatever happened, the last read didn't actually read anything for + // us. This is effectively a retry. + + PROTOCOL("Error SSL_ERROR_WANT_READ: Repeating as per protocol."); + + continue; + } + + case SSL_ERROR_WANT_X509_LOOKUP: + { + // Perform another read. The read should take care of this. + + PROTOCOL("Error SSL_ERROR_WANT_X509_LOOKUP: Repeating as per protocol."); + + continue; + } + + case SSL_ERROR_SYSCALL: + { + if(bytesRead == -1) + { + SocketException ex(__FILE__, __LINE__); + ex.error = getSocketErrno(); + throw ex; + } + else + { + string errorString = "SSL_ERROR_SYSCALL"; + + EXCEPTION(errorString); + + throw ShutdownException(errorString.c_str(), __FILE__, __LINE__); + } + } + + case SSL_ERROR_SSL: + { + string errorString = "SSL_ERROR_SSL"; + + EXCEPTION(errorString); + + throw ShutdownException(errorString.c_str(), __FILE__, __LINE__); + } + + case SSL_ERROR_ZERO_RETURN: + { + string errorString = "SSL_ERROR_ZERO_RETURN"; + + EXCEPTION(errorString); + + throw ShutdownException(errorString.c_str(), __FILE__, __LINE__); + } + } + } + } + + METHOD_RET("OpenSSL::Connection::readSSL()"); + + return totalBytesRead; +} + +void +IceSecurity::Ssl::OpenSSL::Connection::showCertificateChain(BIO* bio) +{ + STACK_OF(X509)* sk; + + // Big nasty buffer + char buffer[4096]; + + if ((sk = SSL_get_peer_cert_chain(_sslConnection)) != 0) + { + BIO_printf(bio,"---\nCertificate chain\n"); + + for (int i = 0; i < sk_X509_num(sk); i++) + { + X509_NAME_oneline(X509_get_subject_name(sk_X509_value(sk,i)), buffer, sizeof(buffer)); + BIO_printf(bio, "%2d s:%s\n", i, buffer); + + X509_NAME_oneline(X509_get_issuer_name(sk_X509_value(sk,i)), buffer, sizeof(buffer)); + BIO_printf(bio, " i:%s\n", buffer); + + PEM_write_bio_X509(bio, sk_X509_value(sk, i)); + } + } + else + { + BIO_printf(bio, "---\nNo peer certificate chain available.\n"); + } +} + +void +IceSecurity::Ssl::OpenSSL::Connection::showPeerCertificate(BIO* bio, const char* connType) +{ + X509* peerCert = 0; + char buffer[4096]; + + if ((peerCert = SSL_get_peer_certificate(_sslConnection)) != 0) + { + BIO_printf(bio, "%s Certificate\n", connType); + PEM_write_bio_X509(bio, peerCert); + + X509_NAME_oneline(X509_get_subject_name(peerCert), buffer, sizeof(buffer)); + BIO_printf(bio, "subject=%s\n", buffer); + + X509_NAME_oneline(X509_get_issuer_name(peerCert), buffer, sizeof(buffer)); + BIO_printf(bio, "issuer=%s\n", buffer); + + EVP_PKEY *pktmp; + pktmp = X509_get_pubkey(peerCert); + BIO_printf(bio,"%s public key is %d bit\n", connType, EVP_PKEY_bits(pktmp)); + EVP_PKEY_free(pktmp); + + X509_free(peerCert); + } + else + { + BIO_printf(bio, "No %s certificate available.\n", connType); + } +} + +void +IceSecurity::Ssl::OpenSSL::Connection::showSharedCiphers(BIO* bio) +{ + char buffer[4096]; + char* strpointer = 0; + + if ((strpointer = SSL_get_shared_ciphers(_sslConnection, buffer, sizeof(buffer))) != 0) + { + // This works only for SSL 2. In later protocol + // versions, the client does not know what other + // ciphers (in addition to the one to be used + // in the current connection) the server supports. + + BIO_printf(bio, "---\nShared Ciphers:\n"); + + int j = 0; + int i = 0; + + while (*strpointer) + { + if (*strpointer == ':') + { + BIO_write(bio, " ", (15-j%25)); + i++; + j=0; + BIO_write(bio, ((i%3)?" ":"\n"), 1); + } + else + { + BIO_write(bio, strpointer, 1); + j++; + } + + strpointer++; + } + + BIO_write(bio,"\n",1); + } +} + +void +IceSecurity::Ssl::OpenSSL::Connection::showSessionInfo(BIO* bio) +{ + if (_sslConnection->hit) + { + BIO_printf(bio, "Reused session-id\n"); + } + + PEM_write_bio_SSL_SESSION(bio, SSL_get_session(_sslConnection)); +} + +void +IceSecurity::Ssl::OpenSSL::Connection::showSelectedCipherInfo(BIO* bio) +{ + const char* str; + SSL_CIPHER* cipher; + + // Show the cipher that was finally selected. + cipher = SSL_get_current_cipher(_sslConnection); + + str = SSL_CIPHER_get_name(cipher); + BIO_printf(bio, "Cipher Version: %s\n", ((str != 0) ? str : "(NONE)")); + + str = SSL_CIPHER_get_version(cipher); + BIO_printf(bio, "Cipher Name: %s\n", ((str != 0) ? str : "(NONE)")); +} + +void +IceSecurity::Ssl::OpenSSL::Connection::showHandshakeStats(BIO* bio) +{ + BIO_printf(bio, "---\nSSL handshake has read %ld bytes and written %ld bytes\n", + BIO_number_read(SSL_get_rbio(_sslConnection)), + BIO_number_written(SSL_get_wbio(_sslConnection))); +} + +void +IceSecurity::Ssl::OpenSSL::Connection::showClientCAList(BIO* bio, const char* connType) +{ + char buffer[4096]; + STACK_OF(X509_NAME)* sk = SSL_get_client_CA_list(_sslConnection); + + if ((sk != 0) && (sk_X509_NAME_num(sk) > 0)) + { + BIO_printf(bio,"---\nAcceptable %s certificate CA names\n", connType); + + for (int i = 0; i < sk_X509_NAME_num(sk); i++) + { + X509_NAME_oneline(sk_X509_NAME_value(sk, i), buffer, sizeof(buffer)); + BIO_write(bio, buffer, strlen(buffer)); + BIO_write(bio,"\n", 1); + } + } + else + { + BIO_printf(bio,"---\nNo %s certificate CA names sent\n", connType); + } +} |