diff options
author | William R. Otte <wotte@dre.vanderbilt.edu> | 2008-03-04 14:51:23 +0000 |
---|---|---|
committer | William R. Otte <wotte@dre.vanderbilt.edu> | 2008-03-04 14:51:23 +0000 |
commit | 99aa8c60282c7b8072eb35eb9ac815702f5bf586 (patch) | |
tree | bda96bf8c3a4c2875a083d7b16720533c8ffeaf4 /ACE/ace/SSL | |
parent | c4078c377d74290ebe4e66da0b4975da91732376 (diff) | |
download | ATCD-99aa8c60282c7b8072eb35eb9ac815702f5bf586.tar.gz |
undoing accidental deletion
Diffstat (limited to 'ACE/ace/SSL')
25 files changed, 5906 insertions, 0 deletions
diff --git a/ACE/ace/SSL/ACE_SSL.pc.in b/ACE/ace/SSL/ACE_SSL.pc.in new file mode 100644 index 00000000000..51c04a18ff1 --- /dev/null +++ b/ACE/ace/SSL/ACE_SSL.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: ACE_SSL +Description: ACE SSL Library +Requires: ACE +Version: @VERSION@ +Libs: -L${libdir} @ACE_TLS_LDFLAGS@ -lACE_SSL @ACE_TLS_LIBS@ +Cflags: -I${includedir} @ACE_TLS_CPPFLAGS@ diff --git a/ACE/ace/SSL/Makefile.am b/ACE/ace/SSL/Makefile.am new file mode 100644 index 00000000000..135fa9b937b --- /dev/null +++ b/ACE/ace/SSL/Makefile.am @@ -0,0 +1,85 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## ./bin/mwc.pl -type automake -noreldefs ACE.mwc + +includedir = @includedir@/ace/SSL +pkgconfigdir = @libdir@/pkgconfig + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + + +## Makefile.SSL.am + +if BUILD_SSL +if !BUILD_ACE_FOR_TAO + +lib_LTLIBRARIES = libACE_SSL.la + +libACE_SSL_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + @ACE_TLS_CPPFLAGS@ \ + -DACE_SSL_BUILD_DLL + +libACE_SSL_la_SOURCES = \ + SSL_Asynch_BIO.cpp \ + SSL_Asynch_Stream.cpp \ + SSL_Context.cpp \ + SSL_SOCK.cpp \ + SSL_SOCK_Acceptor.cpp \ + SSL_SOCK_Connector.cpp \ + SSL_SOCK_Stream.cpp + +libACE_SSL_la_LDFLAGS = \ + -release @ACE_VERSION_NAME@ @ACE_TLS_LDFLAGS@ + +libACE_SSL_la_LIBADD = \ + $(ACE_BUILDDIR)/ace/libACE.la \ + @ACE_TLS_LIBS@ + +nobase_include_HEADERS = \ + SSL_Asynch_BIO.h \ + SSL_Asynch_Stream.h \ + SSL_Context.h \ + SSL_Context.inl \ + SSL_Export.h \ + SSL_SOCK.h \ + SSL_SOCK.inl \ + SSL_SOCK_Acceptor.h \ + SSL_SOCK_Acceptor.inl \ + SSL_SOCK_Connector.h \ + SSL_SOCK_Connector.inl \ + SSL_SOCK_Stream.h \ + SSL_SOCK_Stream.inl \ + sslconf.h + +pkgconfig_DATA = \ + ACE_SSL.pc + +CLEANFILES = \ + ACE_SSL.pc + +ACE_SSL.pc: ${top_builddir}/config.status ${srcdir}/ACE_SSL.pc.in + ${top_builddir}/config.status --file $@:${srcdir}/ACE_SSL.pc.in + +endif !BUILD_ACE_FOR_TAO +endif BUILD_SSL + +EXTRA_DIST = \ + ACE_SSL.pc.in + + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/ace/SSL/SSL_Asynch_BIO.cpp b/ACE/ace/SSL/SSL_Asynch_BIO.cpp new file mode 100644 index 00000000000..b287cf80ac8 --- /dev/null +++ b/ACE/ace/SSL/SSL_Asynch_BIO.cpp @@ -0,0 +1,252 @@ +// -*- C++ -*- + +#include "SSL_Asynch_BIO.h" + +#if OPENSSL_VERSION_NUMBER > 0x0090581fL && ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) + +#include "SSL_Asynch_Stream.h" +#include "ace/OS_NS_string.h" + + +ACE_RCSID (ACE_SSL, + SSL_Asynch_BIO, + "$Id$") + + +#if (defined (ACE_HAS_VERSIONED_NAMESPACE) && ACE_HAS_VERSIONED_NAMESPACE == 1) +# define ACE_ASYNCH_BIO_WRITE_NAME ACE_PREPROC_CONCATENATE(ACE_VERSIONED_NAMESPACE_NAME, _ACE_Asynch_BIO_write) +# define ACE_ASYNCH_BIO_READ_NAME ACE_PREPROC_CONCATENATE(ACE_VERSIONED_NAMESPACE_NAME, _ACE_Asynch_BIO_read) +# define ACE_ASYNCH_BIO_PUTS_NAME ACE_PREPROC_CONCATENATE(ACE_VERSIONED_NAMESPACE_NAME, _ACE_Asynch_BIO_puts) +# define ACE_ASYNCH_BIO_CTRL_NAME ACE_PREPROC_CONCATENATE(ACE_VERSIONED_NAMESPACE_NAME, _ACE_Asynch_BIO_ctrl) +# define ACE_ASYNCH_BIO_NEW_NAME ACE_PREPROC_CONCATENATE(ACE_VERSIONED_NAMESPACE_NAME, _ACE_Asynch_BIO_new) +# define ACE_ASYNCH_BIO_FREE_NAME ACE_PREPROC_CONCATENATE(ACE_VERSIONED_NAMESPACE_NAME, _ACE_Asynch_BIO_free) +#else +# define ACE_ASYNCH_BIO_WRITE_NAME ACE_Asynch_BIO_write +# define ACE_ASYNCH_BIO_READ_NAME ACE_Asynch_BIO_read +# define ACE_ASYNCH_BIO_PUTS_NAME ACE_Asynch_BIO_puts +# define ACE_ASYNCH_BIO_CTRL_NAME ACE_Asynch_BIO_ctrl +# define ACE_ASYNCH_BIO_NEW_NAME ACE_Asynch_BIO_new +# define ACE_ASYNCH_BIO_FREE_NAME ACE_Asynch_BIO_free +#endif /* ACE_HAS_VERSIONED_NAMESPACE == 1 */ + +/** + * @name OpenSSL BIO Helper Methods for use with ACE's Asynchronous + * SSL I/O support. + */ +//@{ +extern "C" +{ + int ACE_ASYNCH_BIO_WRITE_NAME (BIO *pBIO, const char *buf, int len); + int ACE_ASYNCH_BIO_READ_NAME (BIO *pBIO, char *buf, int len); + int ACE_ASYNCH_BIO_PUTS_NAME (BIO *pBIO, const char *str); + long ACE_ASYNCH_BIO_CTRL_NAME (BIO *pBIO, int cmd, long arg1, void *arg2); + int ACE_ASYNCH_BIO_NEW_NAME (BIO *pBIO); + int ACE_ASYNCH_BIO_FREE_NAME (BIO *pBIO); +} +//@} + +#define BIO_TYPE_ACE ( 21 | BIO_TYPE_SOURCE_SINK ) + +static BIO_METHOD methods_ACE = + { + BIO_TYPE_ACE, // BIO_TYPE_PROXY_SERVER, + "ACE_Asynch_BIO", + ACE_ASYNCH_BIO_WRITE_NAME, + ACE_ASYNCH_BIO_READ_NAME, + ACE_ASYNCH_BIO_PUTS_NAME, + 0, /* ACE_ASYNCH_BIO_GETS_NAME, */ + ACE_ASYNCH_BIO_CTRL_NAME, + ACE_ASYNCH_BIO_NEW_NAME, + ACE_ASYNCH_BIO_FREE_NAME, + 0 + }; + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +BIO * +ACE_SSL_make_BIO (void * ssl_asynch_stream) +{ + BIO * const pBIO = BIO_new (&methods_ACE); + + if (pBIO) + BIO_ctrl (pBIO, + BIO_C_SET_FILE_PTR, + 0, + ssl_asynch_stream); + + return pBIO; +} + +/** + * @struct @c ACE_SSL_Asynch_Stream_Accessor + * + * @brief Privileged @c ACE_SSL_Asynch_Stream accessor. + * + * This structure is a @c friend to the @c ACE_SSL_Asynch_Stream + * class so that it can gain access to the protected + * ssl_bio_{read,write}() methods in that class. It is full declared + * in this implementation file to hide its interface from users to + * prevent potential abuse of the friend relationship between it and + * the @c ACE_SSL_Asynch_Stream class. + */ +struct ACE_SSL_Asynch_Stream_Accessor +{ + static int read (ACE_SSL_Asynch_Stream * stream, + char * buf, + size_t len, + int & errval) + { + return stream->ssl_bio_read (buf, len, errval); + } + + static int write (ACE_SSL_Asynch_Stream * stream, + const char * buf, + size_t len, + int & errval) + { + return stream->ssl_bio_write (buf, len, errval); + } +}; + +ACE_END_VERSIONED_NAMESPACE_DECL + +int +ACE_ASYNCH_BIO_NEW_NAME (BIO * pBIO) +{ + pBIO->init = 0; // not initialized + pBIO->num = 0; // still zero ( we can use it ) + pBIO->ptr = 0; // will be pointer to ACE_SSL_Asynch_Stream + pBIO->flags = 0; // + + return 1; +} + +int +ACE_ASYNCH_BIO_FREE_NAME (BIO * pBIO) +{ + if (pBIO && pBIO->shutdown) + { + pBIO->ptr = 0; + pBIO->init = 0; + pBIO->num = 0; + pBIO->flags = 0; + + return 1; + } + + return 0; +} + +int +ACE_ASYNCH_BIO_READ_NAME (BIO * pBIO, char * buf, int len) +{ + BIO_clear_retry_flags (pBIO); + + ACE_SSL_Asynch_Stream * const p_stream = + static_cast<ACE_SSL_Asynch_Stream *> (pBIO->ptr); + + if (pBIO->init == 0 || p_stream == 0 || buf == 0 || len <= 0) + return -1; + + BIO_clear_retry_flags (pBIO); + + int errval = 0; + + int retval = + ACE_SSL_Asynch_Stream_Accessor::read (p_stream, + buf, + len, + errval); + + if (retval >= 0) + return retval; + + if (errval == EINPROGRESS) + BIO_set_retry_read (pBIO); + + return -1; +} + +int +ACE_ASYNCH_BIO_WRITE_NAME (BIO * pBIO, const char * buf, int len) +{ + BIO_clear_retry_flags (pBIO); + + ACE_SSL_Asynch_Stream * p_stream = + static_cast<ACE_SSL_Asynch_Stream *> (pBIO->ptr); + + if (pBIO->init == 0 || p_stream == 0 || buf == 0 || len <= 0) + return -1; + + BIO_clear_retry_flags (pBIO); + + int errval = 0; + + int retval = + ACE_SSL_Asynch_Stream_Accessor::write (p_stream, + buf, + len, + errval); + + if (retval >= 0) + return retval; + + if (errval == EINPROGRESS) + BIO_set_retry_write (pBIO); + + return -1; +} + +long +ACE_ASYNCH_BIO_CTRL_NAME (BIO * pBIO, int cmd, long num, void *ptr) +{ + long ret = 1; + + switch (cmd) + { + case BIO_C_SET_FILE_PTR: + pBIO->shutdown = static_cast<int> (num); + pBIO->ptr = ptr; + pBIO->init = 1; + break; + + case BIO_CTRL_INFO: + ret = 0; + break; + + case BIO_CTRL_GET_CLOSE: + ret = pBIO->shutdown; + break; + + case BIO_CTRL_SET_CLOSE: + pBIO->shutdown = static_cast<int> (num); + break; + + case BIO_CTRL_PENDING: + case BIO_CTRL_WPENDING: + ret = 0; + break; + + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: + ret = 1; + break; + + default: + ret = 0; + break; + } + + return ret; +} + +int +ACE_ASYNCH_BIO_PUTS_NAME (BIO *pBIO, const char *str) +{ + size_t const n = ACE_OS::strlen (str); + + return ACE_ASYNCH_BIO_WRITE_NAME (pBIO, str, n); +} + +#endif /* OPENSSL_VERSION_NUMBER > 0x0090581fL && (ACE_WIN32 || + ACE_HAS_AIO_CALLS) */ diff --git a/ACE/ace/SSL/SSL_Asynch_BIO.h b/ACE/ace/SSL/SSL_Asynch_BIO.h new file mode 100644 index 00000000000..22c6202754b --- /dev/null +++ b/ACE/ace/SSL/SSL_Asynch_BIO.h @@ -0,0 +1,42 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file SSL_Asynch_BIO.h + * + * $Id$ + * + * @author Alexander Libman <alibman@baltimore.com> + * @author Ossama Othman <ossama@uci.edu> + * + */ +//============================================================================= + +#ifndef ACE_SSL_ASYNCH_BIO_H +#define ACE_SSL_ASYNCH_BIO_H + +#include /**/ "ace/pre.h" + +#include "SSL_Export.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +#pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// This must be included before any <openssl> include on LynxOS +#include "ace/os_include/os_stdio.h" + +#include <openssl/bio.h> + +#if OPENSSL_VERSION_NUMBER > 0x0090581fL && ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL +extern BIO * ACE_SSL_make_BIO (void * ssl_asynch_stream); +ACE_END_VERSIONED_NAMESPACE_DECL + +#endif /* OPENSSL_VERSION_NUMBER > 0x0090581fL (ACE_WIN32 || + ACE_HAS_AIO_CALLS) */ + +#include /**/ "ace/post.h" + +#endif /* ACE_SSL_ASYNCH_BIO_H */ diff --git a/ACE/ace/SSL/SSL_Asynch_Stream.cpp b/ACE/ace/SSL/SSL_Asynch_Stream.cpp new file mode 100644 index 00000000000..877125d19c4 --- /dev/null +++ b/ACE/ace/SSL/SSL_Asynch_Stream.cpp @@ -0,0 +1,1042 @@ +#include "SSL_Asynch_Stream.h" + +ACE_RCSID (ACE_SSL, + SSL_Asynch_Stream, + "$Id$") + +// This only works on platforms with Asynchronous IO support. +#if OPENSSL_VERSION_NUMBER > 0x0090581fL && ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) + +#if defined (ACE_WIN32) +# include "ace/WIN32_Proactor.h" +#else +# include "ace/POSIX_Proactor.h" +#endif /* ACE_WIN32 */ + +#include "ace/OS_NS_string.h" +#include "ace/Proactor.h" + +#include <openssl/err.h> + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_SSL_Asynch_Write_Stream_Result::ACE_SSL_Asynch_Write_Stream_Result + (ACE_Handler & handler, + ACE_HANDLE handle, + ACE_Message_Block & message_block, + size_t bytes_to_write, + const void * act, + ACE_HANDLE event, + int priority, + int signal_number + ) + : AWS_RESULT (handler.proxy (), + handle, + message_block, + bytes_to_write, + act, + event, + priority, + signal_number + ) +{ +} + +ACE_SSL_Asynch_Read_Stream_Result::ACE_SSL_Asynch_Read_Stream_Result + (ACE_Handler & handler, + ACE_HANDLE handle, + ACE_Message_Block & message_block, + size_t bytes_to_read, + const void * act, + ACE_HANDLE event, + int priority, + int signal_number + ) + : ARS_RESULT (handler.proxy (), + handle, + message_block, + bytes_to_read, + act, + event, + priority, + signal_number + ) +{ +} + +ACE_SSL_Asynch_Result::ACE_SSL_Asynch_Result (ACE_Handler & handler) + : A_RESULT (handler.proxy (), + 0, // act, + ACE_INVALID_HANDLE, + 0, // Offset + 0, // OffsetHigh + 0, // Priority + ACE_SIGRTMIN //signal_number + ) +{ +} + +void +ACE_SSL_Asynch_Result::complete (size_t /* bytes_transferred */, + int /* success */, + const void * /* completion_key */, + u_long /* error */) +{ + this->handler_proxy_->handler ()->handle_wakeup (); +} + +// ************************************************************ +// ACE_SSL_Asynch_Stream Constructor / Destructor +// ************************************************************ +ACE_SSL_Asynch_Stream::ACE_SSL_Asynch_Stream ( + ACE_SSL_Asynch_Stream::Stream_Type s_type, + ACE_SSL_Context * context) + : type_ (s_type), + handle_ (ACE_INVALID_HANDLE), + proactor_ (0), + ext_handler_ (0), + ext_read_result_ (0), + ext_write_result_(0), + flags_ (0), + ssl_ (0), + bio_ (0), + bio_istream_ (), + bio_inp_msg_ (), + bio_inp_errno_(0), + bio_inp_flag_ (0), + bio_ostream_ (), + bio_out_msg_ (), + bio_out_errno_(0), + bio_out_flag_ (0), + mutex_ () +{ + ACE_TRACE ("ACE_SSL_Asynch_Stream::ACE_SSL_Asynch_Stream"); + // was honestly copied from ACE_SSL_SOCK_Stream :) + + ACE_SSL_Context * ctx = + (context == 0 ? ACE_SSL_Context::instance () : context); + + this->ssl_ = ::SSL_new (ctx->context ()); + + if (this->ssl_ == 0) + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("(%P|%t) ACE_SSL_Asynch_Stream %p\n"), + ACE_TEXT ("- cannot allocate new SSL structure") + )); + +} + +ACE_SSL_Asynch_Stream::~ACE_SSL_Asynch_Stream (void) +{ + ACE_TRACE ("ACE_SSL_Asynch_Stream::~ACE_SSL_Asynch_Stream"); + + + // It is safe to delete stream if all notifications are received, + // i.e., state is SF_DELETE_ENABLE or if proactor event loop is + // done. + if (this->flags_ & SF_STREAM_OPEN) // open + if ((this->flags_ & SF_DELETE_ENABLE) == 0) // but .. + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("ACE_SSL_Asynch_Stream::DTOR-") + ACE_TEXT("possible access violation ") + ACE_TEXT("if proactor still handles events\n"))); + + ::SSL_free (this->ssl_); + + // Was honestly copied from ACE_SSL_SOCK_Stream :) + + // @@ Question: should we reference count the Context object or + // leave that to the application developer? We do not reference + // count reactors (for example) and following some simple rules + // seems to work fine! +} + +// ************************************************************ +// close () +// returns : +// 0 - Stream is in the state SF_DELETE_ENABLE, +// so it is safe to delete stream +// -1 - Stream has pending AIO requests, +// close should be repeated later one more +// ************************************************************ + +int +ACE_SSL_Asynch_Stream::close (void) +{ + ACE_MT (ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1)); + + if ((flags_ & SF_STREAM_OPEN) == 0) // not open + flags_ |= SF_DELETE_ENABLE; + + if (flags_ & SF_DELETE_ENABLE) + return 0; + + flags_ |= SF_REQ_SHUTDOWN; + + this->do_SSL_state_machine (); + + return -1; +} + +// ************************************************************ +// Asynch_Operation interface +// cancel +// ************************************************************ +int +ACE_SSL_Asynch_Stream::cancel (void) +{ + ACE_MT (ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1)); + + if ((flags_ & SF_STREAM_OPEN) == 0) // not open + return 1; // AIO_ALLDONE + + // attempt to cancel internal, i.e. user's read/write + int rc_r_int = bio_istream_.cancel(); + int rc_w_int = bio_ostream_.cancel(); + + // attempt to cancel external, i.e. bio_ssl read/write + int rc_r_ext = notify_read (0, ERR_CANCELED); + int rc_w_ext = notify_write (0, ERR_CANCELED); + + if (rc_r_int < 0 || rc_w_int < 0 + && rc_r_ext < 0 || rc_w_ext < 0) + return -1; // at least one error + + if (rc_r_int == 1 && rc_w_int == 1 + && rc_r_ext == 1 && rc_w_ext == 1) + return 1; // AIO_ALLDONE + + if (rc_r_int == 2 || rc_w_int == 2 + && rc_r_ext == 2 || rc_w_ext == 2) + return 2; // AIO_NOT_CANCELED , at least one not canceled + + return 0; // AIO_CANCELED, at least will be one notification +} + +// ************************************************************ +// Asynch_Operation interface +// open +// ************************************************************ +int +ACE_SSL_Asynch_Stream::open (ACE_Handler & handler, + ACE_HANDLE handle, + const void * completion_key, + ACE_Proactor * proactor) +{ + ACE_MT (ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1)); + + if (this->flags_ & SF_STREAM_OPEN) + ACE_ERROR_RETURN + ((LM_ERROR, + ACE_TEXT ("(%P|%t) ACE_SSL_Asynch_Stream::open() %p\n"), + ACE_TEXT ("- already opened")), + -1); + + if (this->ssl_ == 0) + ACE_ERROR_RETURN + ((LM_ERROR, + ACE_TEXT ("(%P|%t) ACE_SSL_Asynch_Stream::open() %p\n"), + ACE_TEXT ("- SSL structure is absent")), + -1); + + if (handle == ACE_INVALID_HANDLE) + ACE_ERROR_RETURN + ((LM_ERROR, + ACE_TEXT ("(%P|%t) ACE_SSL_Asynch_Stream::open() %p\n"), + ACE_TEXT ("- invalid handle")), + -1); + + + // Get a proactor for/from the user. + this->proactor_ = this->get_proactor (proactor, handler); + this->ext_handler_ = & handler; + this->handle_ = handle; + + // Open internal input stream + if (this->bio_istream_.open (*this, // real callbacks to this + handle, + completion_key, + this->proactor_) != 0) + return -1; + + // Open internal output stream + if (this->bio_ostream_.open (*this, // real callbacks to this + handle, + completion_key, + this->proactor_) != 0) + return -1; + + this->bio_ = ACE_SSL_make_BIO (this); + + if (this->bio_ == 0) + ACE_ERROR_RETURN + ((LM_ERROR, + ACE_TEXT ("(%P|%t) ACE_SSL_Asynch_Stream::open() %p\n"), + ACE_TEXT ("- cannot allocate new BIO structure")), + -1); + + ::SSL_set_bio (this->ssl_ , this->bio_ , this->bio_); + + switch (this->type_) + { + case ST_CLIENT: + ::SSL_set_connect_state (this->ssl_); + break; + + case ST_SERVER: + ::SSL_set_accept_state (this->ssl_); + break; + + default: + ACE_ERROR_RETURN + ((LM_ERROR, + ACE_TEXT ("(%P|%t) ACE_SSL_Asynch_Stream::open() %p\n"), + ACE_TEXT ("- invalid stream type")), + -1); + } + + this->flags_ |= SF_STREAM_OPEN; + + this->do_SSL_state_machine (); + + return 0; +} + + +// ************************************************************ +// Asynch_Operation interface +// read +// ************************************************************ +int +ACE_SSL_Asynch_Stream::read (ACE_Message_Block & message_block, + size_t bytes_to_read, + const void * act, + int priority, + int signal_number) +{ + ACE_MT (ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1)); + + if ((this->flags_ & SF_STREAM_OPEN) == 0) // not open + return -1; + + if (this->flags_ & SF_REQ_SHUTDOWN) + return -1; + + // only one read operation is allowed now + // later it will be possible to make a queue + + if (this->ext_read_result_ != 0) + return -1; + + // create result for future notification + ACE_NEW_RETURN (this->ext_read_result_, + ACE_SSL_Asynch_Read_Stream_Result ( + *this->ext_handler_, + this->handle_, + message_block, + bytes_to_read, + act, + this->proactor_->get_handle(), + priority, + signal_number), + -1); + + this->do_SSL_state_machine (); // ignore return code + + return 0; +} + +// ************************************************************ +// Asynch_Operation interface +// write +// ************************************************************ +int +ACE_SSL_Asynch_Stream::write (ACE_Message_Block & message_block, + size_t bytes_to_write, + const void * act, + int priority, + int signal_number) +{ + ACE_MT (ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1)); + + if ((this->flags_ & SF_STREAM_OPEN) == 0) // not open + return -1; + + if (this->flags_ & SF_REQ_SHUTDOWN) + return -1; + + // only one read operation is allowed now + // later it will be possible to make a queue + + if (this->ext_write_result_ != 0) + return -1; + + // create result for future notification + ACE_NEW_RETURN (this->ext_write_result_, + ACE_SSL_Asynch_Write_Stream_Result ( + *this->ext_handler_, + this->handle_, + message_block, + bytes_to_write, + act, + this->proactor_->get_handle(), + priority, + signal_number), + -1); + + this->do_SSL_state_machine (); + + return 0; +} + +// ************************************************************ +// Main SSL engine +// ************************************************************ +int +ACE_SSL_Asynch_Stream::do_SSL_state_machine (void) +{ + // this protected member should be called + // with locked mutex_ + + int retval = this->do_SSL_handshake (); + + if (retval == 0) // handshake in progress ? + return 0; + + if (retval < 0) + this->flags_ |= SF_REQ_SHUTDOWN; + + this->do_SSL_read (); // execute user read request + this->do_SSL_write (); // execute user write request + + if ((this->flags_ & SF_REQ_SHUTDOWN) == 0) // Do we have any errors + return 0; + + this->do_SSL_shutdown (); + + this->notify_close (); + + return 0; +} + +// ************************************************************ +// do_SSL_shutdown +// return code: +// 1 SSL shutdown is finished already, success +// 0 SSL shutdown in progress +// -1 failure +// ************************************************************ +int +ACE_SSL_Asynch_Stream::do_SSL_shutdown (void) +{ + if (this->flags_ & SF_SHUTDOWN_DONE) // already done + return 1; + + this->flags_ |= SF_REQ_SHUTDOWN; + + // if we have any uncompleted user requests + // than cancel its + this->notify_read (0, ERR_CANCELED); + this->notify_write (0, ERR_CANCELED); + + int retval = ::SSL_shutdown (this->ssl_); + + int status = ::SSL_get_error (this->ssl_, retval); + + switch (status) + { + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_SYSCALL: + retval = 1; + break; + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_CONNECT: + // case SSL_ERROR_WANT_ACCEPT: + case SSL_ERROR_WANT_X509_LOOKUP: + return 0; + + default: + this->print_error (status, + ACE_TEXT ("Shutdown error")); + retval = -1; + break; + } + + this->flags_ |= SF_SHUTDOWN_DONE; + + return retval; +} + +// ************************************************************ +// Do SSL handshake if necessary +// return code: +// 1 SSL handshake is finished already, success +// 0 SSL handshake in progress +// -1 failure +// ************************************************************ +int +ACE_SSL_Asynch_Stream::do_SSL_handshake (void) +{ + if (SSL_is_init_finished (this->ssl_)) + return 1; + + if (this->flags_ & SF_REQ_SHUTDOWN) + return -1; + + int retval = -1; + + switch (this->type_) + { + case ST_CLIENT: + retval = ::SSL_connect (this->ssl_); + break; + + case ST_SERVER: + retval = ::SSL_accept (this->ssl_); + break; + + default: + ACE_ERROR_RETURN + ((LM_ERROR, + ACE_TEXT ("(%P|%t) ACE_SSL_Asynch_Stream %p\n"), + ACE_TEXT ("- invalid stream type")), + -1); + } + + int status = ::SSL_get_error (this->ssl_, retval); + + switch (status) + { + case SSL_ERROR_NONE: + break; + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_CONNECT: + //case SSL_ERROR_WANT_ACCEPT: + case SSL_ERROR_WANT_X509_LOOKUP: + return 0; + + case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_SYSCALL: + default: + this->print_error (status, + ACE_TEXT ("Handshake error")); + return -1; + } + + return 1; +} + +// ************************************************************ +// Perform SSL_read call if necessary and notify user +// ************************************************************ +int +ACE_SSL_Asynch_Stream::do_SSL_read (void) +{ + if (this->ext_read_result_ == 0) // nothing to do + return 0; + + if (this->flags_ & SF_REQ_SHUTDOWN) + { + this->notify_read (0, ERR_CANCELED); + return -1; + } + + ACE_Message_Block & mb = this->ext_read_result_->message_block (); + size_t bytes_req = this->ext_read_result_->bytes_to_read (); + + ERR_clear_error(); + + const int bytes_trn = ::SSL_read (this->ssl_, + mb.wr_ptr (), + bytes_req); + + int const status = ::SSL_get_error (this->ssl_, bytes_trn); + + switch (status) + { + case SSL_ERROR_NONE: + this->notify_read (bytes_trn, 0); + return 1; + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + + case SSL_ERROR_ZERO_RETURN: + this->notify_read (0, 0); + return 1; + + case SSL_ERROR_SYSCALL: + if (bytes_trn == 0) + { + this->notify_read (0, 0); + return 1; + } + // If not an EOF, then fall through to "default" case. + + default: + break; + } + + this->notify_read (0, EFAULT); + this->print_error (status, + ACE_TEXT ("SSL_read error")); + + return -1; +} + +// ************************************************************ +// Perform SSL_write call if necessary and notify user +// ************************************************************ +int +ACE_SSL_Asynch_Stream::do_SSL_write (void) +{ + if (this->ext_write_result_ == 0) // nothing to do + return 0; + + if (this->flags_ & SF_REQ_SHUTDOWN) + { + this->notify_write (0, ERR_CANCELED); + return -1; + } + + ACE_Message_Block & mb = this->ext_write_result_->message_block (); + size_t bytes_req = this->ext_write_result_->bytes_to_write (); + + ERR_clear_error(); + + const int bytes_trn = ::SSL_write (this->ssl_, + mb.rd_ptr (), + bytes_req); + + int const status = ::SSL_get_error (this->ssl_, bytes_trn); + + switch (status) + { + case SSL_ERROR_NONE: + this->notify_write (bytes_trn, 0); + return 1; + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + + case SSL_ERROR_ZERO_RETURN: + this->notify_write (bytes_trn, 0); + return 1; + + case SSL_ERROR_SYSCALL: + default: + break; + } + + this->notify_write(0, EFAULT); + this->print_error (status, + ACE_TEXT ("SSL_write error")); + + return -1; +} + +// ************************************************************ +// notify external user handler that +// it is now to safe destroy stream +// Return code looks like cancel() return code +// 0 - notified NOTIFIED +// 1 - nothing to notify ALLDONE +// 2 - unable to notify NOT NOTIFIED +// ************************************************************ +int +ACE_SSL_Asynch_Stream::notify_close (void) +{ + if (this->flags_ & SF_CLOSE_NTF_SENT) // already sent + return 1; + + if ((this->flags_ & SF_SHUTDOWN_DONE) == 0) // only after shutdown + return 2; // too early , we will do later + + if (this->pending_BIO_count () != 0) // wait for all internal IO + return 2; // too early , we will do later + + // create result for future notification + ACE_SSL_Asynch_Result * close_result = 0; + + ACE_NEW_RETURN (close_result, + ACE_SSL_Asynch_Result (*this), + 2); + //@@ Not exception safe! + + int retval = + close_result->post_completion (this->proactor_->implementation ()); + + if (retval == 0) + { + this->flags_ |= SF_CLOSE_NTF_SENT; + return 0; + } + + delete close_result; + return 2; +} + +// ************************************************************ +// notify external user handler about user write completion +// Return code looks like cancel() return code +// 0 - notified NOTIFIED/CANCELED +// 1 - nothing to notify ALLDONE +// 2 - unable to notify NOT NOTIFIED/CANCELED +// ************************************************************ + +int +ACE_SSL_Asynch_Stream::notify_read (int bytes_transferred, + int error) +{ + if (ext_read_result_ == 0) //nothing to notify + return 1; + + this->ext_read_result_->set_bytes_transferred (bytes_transferred); + this->ext_read_result_->set_error (error); + + int retval = + this->ext_read_result_->post_completion (proactor_->implementation ()); + + if (retval == 0) + { + this->ext_read_result_ = 0; + return 0; // success + } + + return 2; // unable to notify +} + +// ************************************************************ +// notify external user handler about user write completion +// Return code looks like cancel() return code +// 0 - notified NOTIFIED/CANCELED +// 1 - nothing to notify ALLDONE +// 2 - unable to notify NOT NOTIFIED/CANCELED +// ************************************************************ + +int +ACE_SSL_Asynch_Stream::notify_write (int bytes_transferred, + int error) +{ + if (this->ext_write_result_ == 0) //nothing to notify + return 1; + + this->ext_write_result_->set_bytes_transferred (bytes_transferred); + this->ext_write_result_->set_error (error); + + int retval = + this->ext_write_result_->post_completion ( + this->proactor_->implementation ()); + + if (retval == 0) + { + this->ext_write_result_ = 0; + return 0; // success + } + + return 2; // unable to notify +} + +// ************************************************************ +// Print SSL errors +// ************************************************************ +void +ACE_SSL_Asynch_Stream::print_error (int err_ssl, + const ACE_TCHAR * pText) +{ + ACE_DEBUG ((LM_DEBUG, + "SSL-error:%d %s\n" , + err_ssl, + pText)); + +#if OPENSSL_VERSION_NUMBER >= 0x0090601fL + // OpenSSL < 0.9.6a doesn't have ERR_error_string_n() function. + unsigned long lerr = 0; + char buf[1024]; + + while ((lerr = ERR_get_error()) != 0) + { + ERR_error_string_n (lerr, buf, sizeof buf); + + ACE_DEBUG ((LM_DEBUG, "%s\n", buf)); + } +#endif /* OPENSSL_VERSION_NUMBER */ +} + +// ************************************************************ +// BIO helper functions +// SSL library will ask BIO to do raw I/O +// BIO will call us to do this +// ************************************************************ +int +ACE_SSL_Asynch_Stream::ssl_bio_read (char * buf, + size_t len, + int & errval) +{ + // We do not have to acquire mutex + // as we called already with locked mutex + // from do_SSL_state_machine() + + errval = 0; + + size_t cur_len = this->bio_inp_msg_.length (); + + if (cur_len > 0) // there are more data buffered + { + const char * rd_ptr = this->bio_inp_msg_.rd_ptr (); + + if (cur_len > len) + cur_len = len; + + ACE_OS::memcpy (buf, rd_ptr, cur_len); + + this->bio_inp_msg_.rd_ptr (cur_len); // go ahead + + return cur_len; + } + + if (this->bio_inp_errno_ != 0) // if was error - it is permanent ! + { + errval = this->bio_inp_errno_; + return -1; + } + + if (this->bio_inp_flag_ & BF_EOS) // End of stream + return 0; + + errval = EINPROGRESS; // SSL will try later + + if (this->bio_inp_flag_ & BF_AIO) // we are busy + return -1; + + if (this->bio_inp_msg_.size (len) != 0) + { + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("%N:%l ((%P|%t) ACE_SSL_Asynch_Stream %p\n"), + ACE_TEXT ("error in ACE_Message_Block::size() ") + )); + + errval = EINVAL; + return -1; + } + + char * base = this->bio_inp_msg_.base (); + + this->bio_inp_msg_.rd_ptr (base); + this->bio_inp_msg_.wr_ptr (base); + + if (this->bio_istream_.read ( + bio_inp_msg_, // message block + len, // priority + 0, // act + 0, // priority + ACE_SIGRTMIN // default signal + ) == -1) + { + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("%N:%l (%P|%t) ACE_SSL_Asynch_Stream %p\n"), + ACE_TEXT ("attempt read failed") + )); + + errval = EINVAL; // may be leave EINPROGRESS ?? + return -1; // to try later + } + + this->bio_inp_flag_ |= BF_AIO; // AIO is active + + return -1; +} + + +int +ACE_SSL_Asynch_Stream::ssl_bio_write (const char * buf, + size_t len, + int & errval) +{ + // We do not have to acquire mutex + // as we called already with locked mutex + // from do_SSL_state_machine + + errval = 0; + + if (this->bio_out_flag_ & BF_AIO) // sorry, we are busy + { + errval = EINPROGRESS; // try later + return -1; + } + + if (this->bio_out_errno_ != 0) // no recovery + { + errval = this->bio_out_errno_; + return -1; + } + + if (this->bio_out_msg_.size (len) != 0) + { + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("%N:%l ((%P|%t) ACE_SSL_Asynch_Stream %p\n"), + ACE_TEXT ("error in ACE_Message_Block::size() ") + )); + + errval = EINVAL; + return -1; + } + + char * base = this->bio_out_msg_.base (); + + this->bio_out_msg_.rd_ptr (base); + this->bio_out_msg_.wr_ptr (base); + + if (this->bio_out_msg_.copy (buf, len) == -1) + { + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("%N:%l ((%P|%t) ACE_SSL_Asynch_Stream %p\n"), + ACE_TEXT ("error in ACE_Message_Block::copy() ") + )); + + errval = EINVAL; + return -1; + } + + + if (this->bio_ostream_.write ( + this->bio_out_msg_, // message block + len, // priority + 0, // act + 0, // priority + ACE_SIGRTMIN // default signal + ) == -1) + { + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("%N:%l ((%P|%t) ACE_SSL_Asynch_Stream %p\n"), + ACE_TEXT ("attempt write failed") + )); + + errval = EINVAL; // may be leave EINPROGRESS ?? + return -1; // to try later + } + + this->bio_out_flag_ |= BF_AIO; // AIO is active + errval = 0; // Ok, go ahead + + return len; +} + +// ************************************************************ +// Internal IO handlers +// virtual from ACE_Service_Handler +// ************************************************************ +void +ACE_SSL_Asynch_Stream::handle_write_stream ( + const ACE_Asynch_Write_Stream::Result &result) +{ + ACE_MT (ACE_GUARD (ACE_SYNCH_MUTEX, ace_mon, this->mutex_)); + + this->bio_out_flag_ &= ~BF_AIO; + + ACE_Message_Block & mb = result.message_block (); + + size_t bytes_req = result.bytes_to_write (); + size_t bytes_trn = result.bytes_transferred (); + u_long errval = result.error (); + size_t len = bytes_req - bytes_trn; + + if (errval != 0) // error ? + this->bio_out_errno_ = errval; // save err code + else if (len > 0) // TCP/IP overloaded ? + { // continue, rd_ptr at right place + if (this->bio_ostream_.write ( + mb, // message block + len, // priority + 0, // act + 0, // priority + ACE_SIGRTMIN // default signal + ) == 0) + { + this->bio_out_flag_ |= BF_AIO; + return; + } + + ACE_ERROR + ((LM_ERROR, + ACE_TEXT ("(%P|%t) ACE_SSL_Asynch_Stream %p\n"), + ACE_TEXT ("attempt write failed") + )); + + this->bio_out_errno_ = EINVAL; + } + + this->do_SSL_state_machine (); + + return; +} + +void +ACE_SSL_Asynch_Stream::handle_read_stream ( + const ACE_Asynch_Read_Stream::Result &result) +{ + ACE_MT (ACE_GUARD (ACE_SYNCH_MUTEX, ace_mon, this->mutex_)); + + this->bio_inp_flag_ &= ~BF_AIO; + + size_t bytes_trn = result.bytes_transferred (); + u_long errval = result.error (); + + if (errval != 0) // error ? + this->bio_inp_errno_ = errval; // save err code + else if (bytes_trn == 0) // end of stream ? + this->bio_inp_flag_ |= BF_EOS; // set flag EOS + + this->do_SSL_state_machine (); + + return; +} + +void +ACE_SSL_Asynch_Stream::handle_wakeup (void) +{ + ACE_Handler * user_handler = 0; + + { + ACE_MT (ACE_GUARD (ACE_SYNCH_MUTEX, ace_mon, this->mutex_)); + + this->flags_ |= SF_DELETE_ENABLE; + + user_handler = this->ext_handler_; + } + + if (user_handler != 0) + user_handler->handle_wakeup(); +} + +int +ACE_SSL_Asynch_Stream::pending_BIO_count (void) +{ + int ret = 0; + + if (this->bio_inp_flag_ & BF_AIO) + ++ret; + + if (this->bio_out_flag_ & BF_AIO) + ++ret; + + return ret; +} + +ACE_END_VERSIONED_NAMESPACE_DECL + +#endif /* OPENSSL_VERSION_NUMBER > 0x0090581fL && (ACE_WIN32 || + ACE_HAS_AIO_CALLS) */ diff --git a/ACE/ace/SSL/SSL_Asynch_Stream.h b/ACE/ace/SSL/SSL_Asynch_Stream.h new file mode 100644 index 00000000000..671cca46c24 --- /dev/null +++ b/ACE/ace/SSL/SSL_Asynch_Stream.h @@ -0,0 +1,425 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file SSL_Asynch_Stream.h + * + * $Id$ + * + * @author Alexander Libman <alibman@baltimore.com> + */ +//============================================================================= + +#ifndef ACE_SSL_ASYNCH_STREAM_H +#define ACE_SSL_ASYNCH_STREAM_H + +#include /**/ "ace/pre.h" +#include "SSL_Context.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +#pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if OPENSSL_VERSION_NUMBER > 0x0090581fL && ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) + +#include "SSL_Asynch_BIO.h" + +#include "ace/Asynch_IO_Impl.h" +#include "ace/Message_Block.h" +#include "ace/Synch_Traits.h" +#include "ace/Thread_Mutex.h" + +/* + * This facility doesn't follow the normal ACE asynch I/O support classes' + * interface/implementation arrangement. It's not needed because rather than + * branching off to platform-specific APIs, all platforms use the OpenSSL + * API. Thus, you can think of this class as the implementation class (for + * OpenSSL) and there's no separate interface class. + * Also, since both read and write operations are defined in one I/O + * factory, there's no single Result class defined as there is for + * ACE_Asynch_Read_Stream, et al. There are separate result classes defined + * for read and write operations. + */ + +#if defined (ACE_WIN32) +# include "ace/WIN32_Asynch_IO.h" +typedef ACE_WIN32_Asynch_Result A_RESULT; +typedef ACE_WIN32_Asynch_Read_Stream_Result ARS_RESULT; +typedef ACE_WIN32_Asynch_Write_Stream_Result AWS_RESULT; + +# define ERR_CANCELED ERROR_OPERATION_ABORTED + +#else +# include "ace/POSIX_Asynch_IO.h" +typedef ACE_POSIX_Asynch_Result A_RESULT; +typedef ACE_POSIX_Asynch_Read_Stream_Result ARS_RESULT; +typedef ACE_POSIX_Asynch_Write_Stream_Result AWS_RESULT; + +# define ERR_CANCELED ECANCELED + +#endif /* ACE_WIN32 */ + + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +class ACE_SSL_Asynch_Stream; // Forward decl for use in result class def. + +/** + * @class ACE_SSL_Asynch_Read_Stream_Result + * + * Result class that communicates result of read operations initiated on + * an ACE_SSL_Asynch_Stream object. + */ +class ACE_SSL_Asynch_Read_Stream_Result : public ARS_RESULT +{ + /// Factory class will have special permissions. + friend class ACE_SSL_Asynch_Stream; + +protected: + ACE_SSL_Asynch_Read_Stream_Result (ACE_Handler &handler, + ACE_HANDLE handle, + ACE_Message_Block &message_block, + size_t bytes_to_read, + const void* act, + ACE_HANDLE event, + int priority, + int signal_number); +}; + +/** + * @class ACE_SSL_Asynch_Write_Stream_Result + * + * Result class that communicates result of write operations initiated on + * an ACE_SSL_Asynch_Stream object. + */ +class ACE_SSL_Asynch_Write_Stream_Result : public AWS_RESULT +{ + /// Factory class will have special permissions. + friend class ACE_SSL_Asynch_Stream; + +protected: + ACE_SSL_Asynch_Write_Stream_Result (ACE_Handler &handler, + ACE_HANDLE handle, + ACE_Message_Block &message_block, + size_t bytes_to_read, + const void* act, + ACE_HANDLE event, + int priority, + int signal_number); +}; + + +/** + * @class ACE_SSL_Asynch_Result + * + * Result class that is used internally for socket close notifications. + */ +class ACE_SSL_Asynch_Result : public A_RESULT +{ +public: + ACE_SSL_Asynch_Result (ACE_Handler &handler); + + void complete (size_t bytes_transferred, + int success, + const void * completion_key, + u_long error); +}; + + +// Only provide forward declarations to prevent possible abuse of the +// friend declarations in ACE_SSL_Asynch_Stream. +struct ACE_SSL_Asynch_Stream_Accessor; + +/** + * @class ACE_SSL_Asynch_Stream + * + * @brief This class is a factory for initiating asynchronous reads + * and writes on an SSL stream. + * + * Once open() is called, multiple asynchronous read and write operations + * can be started using this class. The handler object (derived from + * ACE_Handler) specified in open() will receive completion events for the + * operations initiated via this class. + */ +class ACE_SSL_Export ACE_SSL_Asynch_Stream + : public ACE_Asynch_Operation, + public ACE_Handler +{ +public: + + // Use a class/struct to work around scoping + // problems for extern "C" free functions with some compilers. For + // example, some can't handle + // + // friend ::some_extern_c_free_function (...) + // + // Note that we could use a straight C++ (i.e. not extern "C") free + // function, but using a class or struct allows us to hide the + // interface from the user, which prevents abuse of this friend + // relationship. + friend struct ACE_SSL_Asynch_Stream_Accessor; + + enum Stream_Type + { + ST_CLIENT = 0x0001, + ST_SERVER = 0x0002 + }; + + /// Constructor. + /** + * @arg context Pointer to an ACE_SSL_Context instance containing + * the OpenSSL information to be associated with this + * ACE_SSL_Asynch_Stream. The needed SSL data will be + * copied before return. Therefore, this object can be + * reused, modified, or deleted upon return. If a 0 pointer + * is passed, the ACE_SSL_Context::instance() method will + * be called to get access to a singleton. + */ + ACE_SSL_Asynch_Stream (Stream_Type s_type = ST_SERVER, + ACE_SSL_Context * context = 0); + + /// Destructor + virtual ~ACE_SSL_Asynch_Stream (void); + + int cancel (void); + + int close (void); + + /** + * Initializes the factory with information which will be used with + * each asynchronous call. + * + * @arg handler The ACE_Handler that will be called to handle completions + * for operations initiated using this factory. + * @arg handle The handle that future read/write operations will use. + * + * @retval 0 for success. + * @retval -1 for failure; consult @c errno for further information. + */ + int open (ACE_Handler &handler, + ACE_HANDLE handle = ACE_INVALID_HANDLE, + const void *completion_key = 0, + ACE_Proactor *proactor = 0); + + /** + * Initiates an asynchronous read. If the operation is successfully + * initiated, the handle_read_stream() method will be called on the + * ACE_Handler object passed to open() when the operation completes. + * Data is read into the specified ACE_Message_Block beginning at its + * write pointer; the block's write pointer is updated to reflect any + * added data when the operation completes. + * + * @arg message_block The specified ACE_Message_Block will receive any + * data that is read. Data will be read into the + * block beginning at the block's write pointer. + * @arg num_bytes_to_read The maximum number of bytes to read. The actual + * amount read may be less. + * @arg act ACT which is passed to the completion handler in + * the result object. + * @arg priority Specifies the operation priority. This has an + * affect on POSIX only. Works like @i nice in Unix. + * Negative values are not allowed. 0 means priority + * of the operation same as the process priority. + * 1 means priority of the operation is one less than + * process, and so forth. This parameter has no + * affect on Win32. + * @arg signal_number The POSIX4 real-time signal number to be used + * for the operation. signal_number ranges from + * ACE_SIGRTMIN to ACE_SIGRTMAX. This argument is + * unused on non-POSIX4 systems. + * + * @retval 0 for success. + * @retval -1 for failure; consult @c errno for further information. + */ + int read (ACE_Message_Block &message_block, + size_t num_bytes_to_read, + const void *act = 0, + int priority = 0, + int signal_number = ACE_SIGRTMIN); + + /** + * Initiates an asynchronous write. If the operation is successfully + * initiated, the handle_write_stream() method will be called on the + * ACE_Handler object passed to open() when the operation completes. + * Data is taken from the specified ACE_Message_Block beginning at its + * read pointer; the block's read pointer is updated to reflect any + * data successfully sent when the operation completes. + * + * @arg message_block The specified ACE_Message_Block is the source of + * data that is written. Data will be taken from the + * block beginning at the block's read pointer. + * @arg bytes_to_write The maximum number of bytes to write. The actual + * amount written may be less. + * @arg act ACT which is passed to the completion handler in + * the result object. + * @arg priority Specifies the operation priority. This has an + * affect on POSIX only. Works like @i nice in Unix. + * Negative values are not allowed. 0 means priority + * of the operation same as the process priority. + * 1 means priority of the operation is one less than + * process, and so forth. This parameter has no + * affect on Win32. + * @arg signal_number The POSIX4 real-time signal number to be used + * for the operation. signal_number ranges from + * ACE_SIGRTMIN to ACE_SIGRTMAX. This argument is + * unused on non-POSIX4 systems. + * + * @retval 0 for success. + * @retval -1 for failure; consult @c errno for further information. + */ + int write (ACE_Message_Block &message_block, + size_t bytes_to_write, + const void *act = 0, + int priority = 0, + int signal_number = ACE_SIGRTMIN); + +protected: + /// Virtual from ACE_Asynch_Operation. Since this class is essentially an + /// implementation class, simply return 0. + virtual ACE_Asynch_Operation_Impl *implementation (void) const { return 0; } + + /// virtual from ACE_Handler + + /// This method is called when BIO write request is completed. It + /// processes the IO completion and calls do_SSL_state_machine(). + virtual void handle_write_stream + (const ACE_Asynch_Write_Stream::Result &result); + + /// This method is called when BIO read request is completed. It + /// processes the IO completion and calls do_SSL_state_machine(). + virtual void handle_read_stream + (const ACE_Asynch_Read_Stream::Result &result); + + /// This method is called when all SSL sessions are closed and no + /// more pending AIOs exist. It also calls users handle_wakeup(). + virtual void handle_wakeup (void); + + /** + * @name SSL State Machine + */ + //@{ + int do_SSL_state_machine (void); + int do_SSL_handshake (void); + int do_SSL_read (void); + int do_SSL_write(void); + int do_SSL_shutdown(void); + //@} + + void print_error (int err_ssl, + const ACE_TCHAR *pText); + + int pending_BIO_count (void); + + /// This method is called to notify user handler when user's read in + /// done. + int notify_read (int bytes_transferred, int error); + + /// This method is called to notify user handler when user's write + /// in done. + int notify_write (int bytes_transferred, int error); + + /// This method is called to notify ourself that SSL session is + /// shutdown and that there is no more I/O activity now and in the + /// future. + int notify_close(void); + + /** + * @name BIO Helpers + */ + //@{ + int ssl_bio_read (char * buf, size_t len, int & errval); + int ssl_bio_write (const char * buf, size_t len, int & errval); + //@} + +private: + + // Preventing copying through construction or assignment. + ACE_SSL_Asynch_Stream (ACE_SSL_Asynch_Stream const &); + ACE_SSL_Asynch_Stream & operator= (ACE_SSL_Asynch_Stream const &); + +protected: + + /// Stream Type ST_CLIENT/ST_SERVER + Stream_Type type_; + + /// The real file/socket handle + ACE_HANDLE handle_; + + /// The proactor + ACE_Proactor * proactor_; + + /// External,i.e user handler + ACE_Handler * ext_handler_; + + /// External, i.e. read result faked for user + ACE_SSL_Asynch_Read_Stream_Result * ext_read_result_ ; + + /// External, i.e. write result faked for user + ACE_SSL_Asynch_Write_Stream_Result * ext_write_result_ ; + + /// Stream state/flags + enum Stream_Flag + { + /// istream_ open OK + SF_STREAM_OPEN = 0x0001, + /// request to SSL shutdown + SF_REQ_SHUTDOWN = 0x0002, + /// SSL shutdown finished + SF_SHUTDOWN_DONE = 0x0004, + /// Close notification sent + SF_CLOSE_NTF_SENT = 0x0008, + /// Stream can be safely destroyed + SF_DELETE_ENABLE = 0x0010 + }; + + int flags_; + + /// The SSL session. + SSL * ssl_; + + /// The BIO implementation + BIO * bio_; + + /// The real streams which work under the ssl connection. + /// BIO performs I/O via this streams + enum BIO_Flag // internal IO flags + { + /// End of stream + BF_EOS = 0x01, + /// Real AIO in progress + BF_AIO = 0x02 + }; + + /** + * @name Internal stream, buffer and info for BIO read + */ + //@{ + ACE_Asynch_Read_Stream bio_istream_; + ACE_Message_Block bio_inp_msg_; + int bio_inp_errno_; + int bio_inp_flag_; + //@} + + /** + * @name Internal stream, buffer and info for BIO write + */ + //@{ + ACE_Asynch_Write_Stream bio_ostream_; + ACE_Message_Block bio_out_msg_; + int bio_out_errno_; + int bio_out_flag_; + //@} + + /// Mutex to protect work + ACE_SYNCH_MUTEX mutex_; + +}; + +ACE_END_VERSIONED_NAMESPACE_DECL + +#endif /* OPENSSL_VERSION_NUMBER > 0x0090581fL && (ACE_WIN32 || + ACE_HAS_AIO_CALLS) */ + +#include /**/ "ace/post.h" + +#endif /* ACE_SSL_ASYNCH_STREAM_H */ diff --git a/ACE/ace/SSL/SSL_Context.cpp b/ACE/ace/SSL/SSL_Context.cpp new file mode 100644 index 00000000000..158f42e80d8 --- /dev/null +++ b/ACE/ace/SSL/SSL_Context.cpp @@ -0,0 +1,636 @@ +#include "SSL_Context.h" + +#include "sslconf.h" + +#if !defined(__ACE_INLINE__) +#include "SSL_Context.inl" +#endif /* __ACE_INLINE__ */ + +#include "ace/Guard_T.h" +#include "ace/Object_Manager.h" +#include "ace/Log_Msg.h" +#include "ace/Singleton.h" +#include "ace/Synch_Traits.h" +#include "ace/ACE.h" +#include "ace/OS_NS_errno.h" +#include "ace/OS_NS_string.h" + +#ifdef ACE_HAS_THREADS +# include "ace/Thread_Mutex.h" +# include "ace/OS_NS_Thread.h" +#endif /* ACE_HAS_THREADS */ + +#include <openssl/x509.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/safestack.h> + +ACE_RCSID (ACE_SSL, + SSL_Context, + "$Id$") + + +namespace +{ + /// Reference count of the number of times the ACE_SSL_Context was + /// initialized. + int ssl_library_init_count = 0; + + // @@ This should also be done with a singleton, otherwise it is not + // thread safe and/or portable to some weird platforms... + +#ifdef ACE_HAS_THREADS + /// Array of mutexes used internally by OpenSSL when the SSL + /// application is multithreaded. + ACE_SSL_Context::lock_type * ssl_locks = 0; + + // @@ This should also be managed by a singleton. +#endif +} + +#ifdef ACE_HAS_THREADS + +# if (defined (ACE_HAS_VERSIONED_NAMESPACE) && ACE_HAS_VERSIONED_NAMESPACE == 1) +# define ACE_SSL_LOCKING_CALLBACK_NAME ACE_PREPROC_CONCATENATE(ACE_VERSIONED_NAMESPACE_NAME, _ACE_SSL_locking_callback) +# define ACE_SSL_THREAD_ID_NAME ACE_PREPROC_CONCATENATE(ACE_VERSIONED_NAMESPACE_NAME, _ACE_SSL_thread_id) +# else +# define ACE_SSL_LOCKING_CALLBACK_NAME ACE_SSL_locking_callback +# define ACE_SSL_THREAD_ID_NAME ACE_SSL_thread_id +# endif /* ACE_HAS_VERSIONED_NAMESPACE == 1 */ + + + +extern "C" +{ + void + ACE_SSL_LOCKING_CALLBACK_NAME (int mode, + int type, + const char * /* file */, + int /* line */) + { + // #ifdef undef + // fprintf(stderr,"thread=%4d mode=%s lock=%s %s:%d\n", + // CRYPTO_thread_id(), + // (mode&CRYPTO_LOCK)?"l":"u", + // (type&CRYPTO_READ)?"r":"w",file,line); + // #endif + // /* + // if (CRYPTO_LOCK_SSL_CERT == type) + // fprintf(stderr,"(t,m,f,l) %ld %d %s %d\n", + // CRYPTO_thread_id(), + // mode,file,line); + // */ + if (mode & CRYPTO_LOCK) + (void) ssl_locks[type].acquire (); + else + (void) ssl_locks[type].release (); + } + + // ------------------------------- + + // Return the current thread ID. OpenSSL uses this on platforms + // that need it. + unsigned long + ACE_SSL_THREAD_ID_NAME (void) + { + return (unsigned long) ACE_VERSIONED_NAMESPACE_NAME::ACE_OS::thr_self (); + } +} +#endif /* ACE_HAS_THREADS */ + + +// **************************************************************** + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +#ifdef ACE_HAS_THREADS +ACE_SSL_Context::lock_type * ACE_SSL_Context::locks_ = 0; +#endif /* ACE_HAS_THREADS */ + +ACE_SSL_Context::ACE_SSL_Context (void) + : context_ (0), + mode_ (-1), + default_verify_mode_ (SSL_VERIFY_NONE), + have_ca_ (0) +{ + ACE_SSL_Context::ssl_library_init (); +} + +ACE_SSL_Context::~ACE_SSL_Context (void) +{ + if (this->context_) + { + ::SSL_CTX_free (this->context_); + this->context_ = 0; + } + + ACE_SSL_Context::ssl_library_fini (); +} + +ACE_SSL_Context * +ACE_SSL_Context::instance (void) +{ + return ACE_Singleton<ACE_SSL_Context, ACE_SYNCH_MUTEX>::instance (); +} + +void +ACE_SSL_Context::ssl_library_init (void) +{ + ACE_MT (ACE_GUARD (ACE_Recursive_Thread_Mutex, + ace_ssl_mon, + *ACE_Static_Object_Lock::instance ())); + + if (ssl_library_init_count == 0) + { + // Initialize the locking callbacks before initializing anything + // else. +#ifdef ACE_HAS_THREADS + int const num_locks = ::CRYPTO_num_locks (); + + this->locks_ = new lock_type[num_locks]; + ssl_locks = this->locks_; + +# if !defined (WIN32) + // This call isn't necessary on some platforms. See the CRYPTO + // library's threads(3) man page for details. + ::CRYPTO_set_id_callback (ACE_SSL_THREAD_ID_NAME); +# endif /* !WIN32 */ + ::CRYPTO_set_locking_callback (ACE_SSL_LOCKING_CALLBACK_NAME); +#endif /* ACE_HAS_THREADS */ + + ::SSLeay_add_ssl_algorithms (); + ::SSL_load_error_strings (); + + // Seed the random number generator. Note that the random + // number generator can be seeded more than once to "stir" its + // state. + +#ifdef WIN32 + // Seed the random number generator by sampling the screen. + ::RAND_screen (); +#endif /* WIN32 */ + +#if OPENSSL_VERSION_NUMBER >= 0x00905100L + // OpenSSL < 0.9.5 doesn't have EGD support. + + const char *egd_socket_file = + ACE_OS::getenv (ACE_SSL_EGD_FILE_ENV); + + if (egd_socket_file != 0) + (void) this->egd_file (egd_socket_file); +#endif /* OPENSSL_VERSION_NUMBER */ + + const char *rand_file = + ACE_OS::getenv (ACE_SSL_RAND_FILE_ENV); + + if (rand_file != 0) + (void) this->seed_file (rand_file); + + // Initialize the mutexes that will be used by the SSL and + // crypto library. + + } + + ++ssl_library_init_count; +} + +void +ACE_SSL_Context::ssl_library_fini (void) +{ + ACE_MT (ACE_GUARD (ACE_Recursive_Thread_Mutex, + ace_ssl_mon, + *ACE_Static_Object_Lock::instance ())); + + --ssl_library_init_count; + if (ssl_library_init_count == 0) + { + ::ERR_free_strings (); + ::EVP_cleanup (); + + // Clean up the locking callbacks after everything else has been + // cleaned up. +#ifdef ACE_HAS_THREADS + ::CRYPTO_set_locking_callback (0); + ssl_locks = 0; + + delete [] this->locks_; + this->locks_ = 0; + +#endif /* ACE_HAS_THREADS */ + } +} + +int +ACE_SSL_Context::set_mode (int mode) +{ + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, + ace_ssl_mon, + *ACE_Static_Object_Lock::instance (), + -1)); + + if (this->context_ != 0) + return -1; + + SSL_METHOD *method = 0; + + switch (mode) + { + case ACE_SSL_Context::SSLv2_client: + method = ::SSLv2_client_method (); + break; + case ACE_SSL_Context::SSLv2_server: + method = ::SSLv2_server_method (); + break; + case ACE_SSL_Context::SSLv2: + method = ::SSLv2_method (); + break; + case ACE_SSL_Context::SSLv3_client: + method = ::SSLv3_client_method (); + break; + case ACE_SSL_Context::SSLv3_server: + method = ::SSLv3_server_method (); + break; + case ACE_SSL_Context::SSLv3: + method = ::SSLv3_method (); + break; + case ACE_SSL_Context::SSLv23_client: + method = ::SSLv23_client_method (); + break; + case ACE_SSL_Context::SSLv23_server: + method = ::SSLv23_server_method (); + break; + case ACE_SSL_Context::SSLv23: + method = ::SSLv23_method (); + break; + case ACE_SSL_Context::TLSv1_client: + method = ::TLSv1_client_method (); + break; + case ACE_SSL_Context::TLSv1_server: + method = ::TLSv1_server_method (); + break; + case ACE_SSL_Context::TLSv1: + method = ::TLSv1_method (); + break; + default: + method = ::SSLv3_method (); + break; + } + + this->context_ = ::SSL_CTX_new (method); + if (this->context_ == 0) + return -1; + + this->mode_ = mode; + + // Load the trusted certificate authority (default) certificate + // locations. But do not return -1 on error, doing so confuses CTX + // allocation (severe error) with the less important loading of CA + // certificate location error. If it is important for your + // application then call ACE_SSL_Context::have_trusted_ca(), + // immediately following this call to set_mode(). + (void) this->load_trusted_ca (); + + return 0; +} + +int +ACE_SSL_Context::load_trusted_ca (const char* ca_file, + const char* ca_dir, + bool use_env_defaults) +{ + this->check_context (); + + if (ca_file == 0 && use_env_defaults) + { + // Use the default environment settings. + ca_file = ACE_OS::getenv (ACE_SSL_CERT_FILE_ENV); + if (ca_file == 0) + ca_file = ACE_DEFAULT_SSL_CERT_FILE; + } + + if (ca_dir == 0 && use_env_defaults) + { + // Use the default environment settings. + ca_dir = ACE_OS::getenv (ACE_SSL_CERT_DIR_ENV); + if (ca_dir == 0) + ca_dir = ACE_DEFAULT_SSL_CERT_DIR; + } + + // NOTE: SSL_CTX_load_verify_locations() returns 0 on error. + if (::SSL_CTX_load_verify_locations (this->context_, + ca_file, + ca_dir) <= 0) + { + if (ACE::debug ()) + ACE_SSL_Context::report_error (); + return -1; + } + + ++this->have_ca_; + + // For TLS/SSL servers scan all certificates in ca_file and ca_dir and + // list them as acceptable CAs when requesting a client certificate. + if (mode_ == SSLv23 + || mode_ == SSLv23_server + || mode_ == TLSv1 + || mode_ == TLSv1_server + || mode_ == SSLv3 + || mode_ == SSLv3_server + || mode_ == SSLv2 + || mode_ == SSLv2_server) + { + // Note: The STACK_OF(X509_NAME) pointer is a copy of the pointer in + // the CTX; any changes to it by way of these function calls will + // change the CTX directly. + STACK_OF (X509_NAME) * cert_names = 0; + cert_names = ::SSL_CTX_get_client_CA_list (this->context_); + bool error = false; + + // Add CAs from both the file and dir, if specified. There should + // already be a STACK_OF(X509_NAME) in the CTX, but if not, we create + // one. + if (ca_file) + { + if (cert_names == 0) + { + if ((cert_names = ::SSL_load_client_CA_file (ca_file)) != 0) + ::SSL_CTX_set_client_CA_list (this->context_, cert_names); + else + error = true; + } + else + { + // Add new certificate names to the list. + error = (0 == ::SSL_add_file_cert_subjects_to_stack (cert_names, + ca_file)); + } + + if (error) + { + if (ACE::debug ()) + ACE_SSL_Context::report_error (); + return -1; + } + } + + // SSL_add_dir_cert_subjects_to_stack is defined at 0.9.8a (but not + // on OpenVMS or Mac Classic); it may be available earlier. Change + // this comparison if so. +#if defined (OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x0090801fL) +# if !defined (OPENSSL_SYS_VMS) && !defined (OPENSSL_SYS_MACINTOSH_CLASSIC) + + if (ca_dir != 0) + { + if (cert_names == 0) + { + if ((cert_names = sk_X509_NAME_new_null ()) == 0) + { + if (ACE::debug ()) + ACE_SSL_Context::report_error (); + return -1; + } + ::SSL_CTX_set_client_CA_list (this->context_, cert_names); + } + if (0 == ::SSL_add_dir_cert_subjects_to_stack (cert_names, ca_dir)) + { + if (ACE::debug ()) + ACE_SSL_Context::report_error (); + return -1; + } + } +# endif /* !OPENSSL_SYS_VMS && !OPENSSL_SYS_MACINTOSH_CLASSIC */ +#endif /* OPENSSL_VERSION_NUMBER >= 0.9.8a release */ + + } + + return 0; +} + + +int +ACE_SSL_Context::private_key (const char *file_name, + int type) +{ + if (this->private_key_.type () != -1) + return 0; + + this->check_context (); + + this->private_key_ = ACE_SSL_Data_File (file_name, type); + + if (::SSL_CTX_use_PrivateKey_file (this->context_, + this->private_key_.file_name (), + this->private_key_.type ()) <= 0) + { + this->private_key_ = ACE_SSL_Data_File (); + return -1; + } + else + return this->verify_private_key (); +} + +int +ACE_SSL_Context::verify_private_key (void) +{ + this->check_context (); + + return (::SSL_CTX_check_private_key (this->context_) <= 0 ? -1 : 0); +} + +int +ACE_SSL_Context::certificate (const char *file_name, + int type) +{ + if (this->certificate_.type () != -1) + return 0; + + this->certificate_ = ACE_SSL_Data_File (file_name, type); + + this->check_context (); + + if (::SSL_CTX_use_certificate_file (this->context_, + this->certificate_.file_name (), + this->certificate_.type ()) <= 0) + { + this->certificate_ = ACE_SSL_Data_File (); + return -1; + } + else + return 0; +} + +int +ACE_SSL_Context::certificate (X509* cert) +{ + // Is it really a good idea to return 0 if we're not setting the + // certificate? + if (this->certificate_.type () != -1) + return 0; + + this->check_context(); + + if (::SSL_CTX_use_certificate (this->context_, cert) <= 0) + { + return -1; + } + else + { + // No file is associated with the certificate, set this to a fictional + // value so we don't reset it later. + this->certificate_ = ACE_SSL_Data_File ("MEMORY CERTIFICATE"); + + return 0; + } +} + +void +ACE_SSL_Context::set_verify_peer (int strict, int once, int depth) +{ + this->check_context (); + + // Setup the peer verififcation mode. + + int verify_mode = SSL_VERIFY_PEER; + if (once) + verify_mode |= SSL_VERIFY_CLIENT_ONCE; + if (strict) + verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + + // set the default verify mode + this->default_verify_mode (verify_mode); + + // Set the max certificate depth but later let the verify_callback + // catch the depth error by adding one to the required depth. + if (depth > 0) + ::SSL_CTX_set_verify_depth (this->context_, depth + 1); +} + + +int +ACE_SSL_Context::random_seed (const char * seed) +{ + ::RAND_seed (seed, ACE_OS::strlen (seed)); + +#if OPENSSL_VERSION_NUMBER >= 0x00905100L + // RAND_status() returns 1 if the PRNG has enough entropy. + return (::RAND_status () == 1 ? 0 : -1); +#else + return 0; // Ugly, but OpenSSL <= 0.9.4 doesn't have RAND_status(). +#endif /* OPENSSL_VERSION_NUMBER >= 0x00905100L */ +} + +int +ACE_SSL_Context::egd_file (const char * socket_file) +{ +#if OPENSSL_VERSION_NUMBER < 0x00905100L + // OpenSSL < 0.9.5 doesn't have EGD support. + ACE_UNUSED_ARG (socket_file); + ACE_NOTSUP_RETURN (-1); +#else + // RAND_egd() returns the amount of entropy used to seed the random + // number generator. The actual value should be greater than 16, + // i.e. 128 bits. + if (::RAND_egd (socket_file) > 0) + return 0; + else + return -1; +#endif /* OPENSSL_VERSION_NUMBER >= 0x00905100L */ +} + +int +ACE_SSL_Context::seed_file (const char * seed_file, long bytes) +{ + // RAND_load_file() returns the number of bytes used to seed the + // random number generator. If the file reads ok, check RAND_status to + // see if it got enough entropy. + if (::RAND_load_file (seed_file, bytes) > 0) +#if OPENSSL_VERSION_NUMBER >= 0x00905100L + // RAND_status() returns 1 if the PRNG has enough entropy. + return (::RAND_status () == 1 ? 0 : -1); +#else + return 0; // Ugly, but OpenSSL <= 0.9.4 doesn't have RAND_status(). +#endif /* OPENSSL_VERSION_NUMBER >= 0x00905100L */ + else + return -1; +} + +void +ACE_SSL_Context::report_error (unsigned long error_code) +{ + if (error_code == 0) + return; + + char error_string[256]; + + (void) ::ERR_error_string (error_code, error_string); + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("ACE_SSL (%P|%t) error code: %u - %C\n"), + error_code, + error_string)); +} + +void +ACE_SSL_Context::report_error (void) +{ + unsigned long err = ::ERR_get_error (); + ACE_SSL_Context::report_error (err); + ACE_OS::last_error (err); +} + +int +ACE_SSL_Context::dh_params (const char *file_name, + int type) +{ + if (this->dh_params_.type () != -1) + return 0; + + // For now we only support PEM encodings + if (type != SSL_FILETYPE_PEM) + return -1; + + this->dh_params_ = ACE_SSL_Data_File (file_name, type); + + this->check_context (); + + { + // Swiped from Rescorla's examples and the OpenSSL s_server.c app + DH * ret=0; + BIO * bio = 0; + + if ((bio = ::BIO_new_file (this->dh_params_.file_name (), "r")) == 0) + { + this->dh_params_ = ACE_SSL_Data_File (); + return -1; + } + + ret = PEM_read_bio_DHparams (bio, 0, 0, 0); + BIO_free (bio); + + if (ret == 0) + { + this->dh_params_ = ACE_SSL_Data_File (); + return -1; + } + + if (::SSL_CTX_set_tmp_dh (this->context_, ret) < 0) + { + this->dh_params_ = ACE_SSL_Data_File (); + return -1; + } + DH_free (ret); + } + + return 0; +} + +// **************************************************************** + +#if defined (ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION) + +template ACE_Singleton<ACE_SSL_Context, ACE_SYNCH_MUTEX> * + ACE_Singleton<ACE_SSL_Context, ACE_SYNCH_MUTEX>::singleton_; + +#endif /* ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION */ + +ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SSL/SSL_Context.h b/ACE/ace/SSL/SSL_Context.h new file mode 100644 index 00000000000..1d7067fa204 --- /dev/null +++ b/ACE/ace/SSL/SSL_Context.h @@ -0,0 +1,384 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file SSL_Context.h + * + * $Id$ + * + * @author Carlos O'Ryan <coryan@ece.uci.edu> + * @author Ossama Othman <ossama@dre.vanderbilt.edu> + */ +//============================================================================= + + +#ifndef ACE_SSL_CONTEXT_H +#define ACE_SSL_CONTEXT_H + +#include /**/ "ace/pre.h" + +#include "SSL_Export.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/SString.h" + +#ifdef ACE_HAS_THREADS +# include "ace/Synch_Traits.h" +#endif /* ACE_HAS_THREADS */ + +#include <openssl/ssl.h> + + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +class ACE_SSL_Export ACE_SSL_Data_File +{ +public: + + /// Default constructor + ACE_SSL_Data_File (void); + + /// Contructor from a file name and the file type. + ACE_SSL_Data_File (const char *file_name, + int type = SSL_FILETYPE_PEM); + + /// The file name + const char *file_name (void) const; + + /// The type + int type (void) const; + +private: + + /// The file name + ACE_CString file_name_; + + /// The type, used by the SSL library to parse the file contents. + int type_; +}; + +// **************************************************************** + + +/** + * @class ACE_SSL_Context + * + * @brief A wrapper for the OpenSSL SSL_CTX related functions. + * + * This class provides a wrapper for the SSL_CTX data structure. + * Since most applications have a single SSL_CTX structure, this class + * can be used as a singleton. + */ +class ACE_SSL_Export ACE_SSL_Context +{ +public: + +#ifdef ACE_HAS_THREADS + typedef ACE_SYNCH_MUTEX lock_type; +#endif /* ACE_HAS_THREADS */ + + enum { + INVALID_METHOD = -1, + SSLv2_client = 1, + SSLv2_server, + SSLv2, + SSLv3_client, + SSLv3_server, + SSLv3, + SSLv23_client, + SSLv23_server, + SSLv23, + TLSv1_client, + TLSv1_server, + TLSv1 + }; + + /// Constructor + ACE_SSL_Context (void); + + /// Destructor + ~ACE_SSL_Context (void); + + /// The Singleton context, the SSL components use the singleton if + /// nothing else is available. + static ACE_SSL_Context *instance (void); + + /** + * Set the CTX mode. The mode can be set only once, afterwards the + * function has no effect and returns -1. + * Once the mode is set the underlying SSL_CTX is initialized and + * the class can be used. + * If the mode is not set, then the class automatically initializes + * itself to the default mode. + */ + int set_mode (int mode = ACE_SSL_Context::SSLv23); + + int get_mode (void) const; + + /// Get the SSL context + SSL_CTX *context (void); + + /// Get the file name and file format used for the private key + int private_key_type (void) const; + const char *private_key_file_name (void) const; + + /// Set the private key file. + /** + * @note This method should only be called after a certificate has + * been set since key verification is performed against the + * certificate, among other things. + */ + int private_key (const char *file_name, int type = SSL_FILETYPE_PEM); + + /// Verify that the private key is valid. + /** + * @note This method should only be called after a certificate has + * been set since key verification is performed against the + * certificate, among other things. + */ + int verify_private_key (void); + + /// Get the file name and file format used for the certificate file + int certificate_type (void) const; + const char *certificate_file_name (void) const; + + /// Set the certificate file. + int certificate (const char *file_name, + int type = SSL_FILETYPE_PEM); + + /// Load certificate from memory rather than a file. + int certificate (X509* cert); + + /** + * Load the location of the trusted certification authority + * certificates. Note that CA certificates are stored in PEM format + * as a sequence of certificates in @a ca_file or as a set of + * individual certificates in @a ca_dir (or both). + * + * Note this method is called by set_mode() to load the default + * environment settings for @a ca_file and @a ca_dir, if any. This + * allows for automatic service configuration (and backward + * compatibility with previous versions). + * + * Note that the underlying SSL function will add valid file and + * directory names to the load location lists maintained as part of + * the SSL_CTX table. It therefore doesn't make sense to keep a + * copy of the file and path name of the most recently added + * @a ca_file or @a ca_path. + * + * @param[in] ca_file CA file pathname. Passed to + * @c SSL_CTX_load_verify_locations() if not + * 0. If 0, behavior depends on the value of + * @a use_env_defaults. + * @param[in] ca_dir CA directory pathname. Passed to + * @c SSL_CTX_load_verify_locations() if not + * 0. If 0, behavior depends on the value of + * @a use_env_defaults. + * @param[in] use_env_defaults If false, the specified @a ca_file argument + * is passed to + * @c SSL_CTX_load_verify_locations(), + * regardless of its value. + * If true (the default), additional defaults + * can be applied to either @a ca_file, + * @a ca_dir, or both. The following + * additional defaults are applied when the + * @a ca_file argument is 0: + * - The @c SSL_CERT_FILE environment variable + * will be queried for a file name to use as + * the @a ca_file argument. The environment + * variable name to query can be changed by + * supplying a @c ACE_SSL_CERT_FILE_ENV + * configuration item when building ACE. + * - If there is no @c SSL_CERT_FILE in the + * current environment, the file specified + * by the @c ACE_DEFAULT_SSL_CERT_FILE ACE + * configuration item will be used. The + * default value is "cert.pem" on Windows + * and "/etc/ssl/cert.pem" on all other + * platforms. + * The following additional defaults are + * applied when the @a ca_dir argument is 0: + * - The @c SSL_CERT_DIR environment variable + * will be queried for a file name to use as + * the @a ca_dir argument. The environment + * variable name to query can be changed by + * supplying a @c ACE_SSL_CERT_DIR_ENV + * configuration item when building ACE. + * - If there is no @c SSL_CERT_DIR in the + * current environment, the directory + * specified by the @c + * ACE_DEFAULT_SSL_CERT_DIR ACE + * configuration item will be used. The + * default value is "certs" on Windows + * and "/etc/ssl/certs" on all other + * platforms. + * + * @return 0 for success or -1 on error. + * + * @see OpenSSL manual SSL_CTX_load_verify_locations(3) for a + * detailed description of the CA file and directory requirements + * and processing. + */ + int load_trusted_ca (const char* ca_file = 0, + const char* ca_dir = 0, + bool use_env_defaults = true); + + /** + * Test whether any CA locations have been successfully loaded and + * return the number of successful attempts. + * + * @retval >0 The number of successful CA load attempts. + * @retval 0 If all CA load attempts have failed. + */ + int have_trusted_ca (void) const; + + + /** + * @todo Complete this documentation where elipses(...) are used + * + * @doc Use this method when certificate chain verification is + * required. The default server behaviour is SSL_VERIFY_NONE + * i.e. client certicates are requested for verified. This method + * can be used to configure server to request client certificates + * and perform the certificate verification. If <strict> is set + * true the client connection is rejected when certificate + * verification fails. Otherwise the session is accepted with a + * warning, which is the default behaviour. If <once> is set true + * (default), certificates are requested only once per session. + * The last parameter <depth> can be used to set the verification + * depth. + * + * Note for verification to work correctly there should be a valid + * CA name list set using load_trusted_ca(). + * + * @see OpenSSL documentation of SSL_CTX_set_verify(3) for details of + * the verification process. + * + * @see OpenSSL documentation ... set_verify_depth(3) ... + * + * Note that this method overrides the use of the + * default_verify_mode() method. + */ + void set_verify_peer (int strict = 0, int once = 1, int depth = 0); + + + /// TODO: a implementation that will lookup the CTX table for the list + /// of files and paths etc. + /// Query the location of trusted certification authority + /// certificates. + // const char* ca_file_name(void) const; + // const char* ca_dir_name(void) const; + + /** + * Set and query the default verify mode for this context, it is + * inherited by all the ACE_SSL objects created using the context. + * It can be overriden on a per-ACE_SSL object. + */ + void default_verify_mode (int mode); + int default_verify_mode (void) const; + + /** + * @name OpenSSL Random Number Generator Seed Related Methods + * + * These are methods that can be used to seed OpenSSL's + * pseudo-random number generator. These methods can be called more + * than once. + */ + //@{ + /// Seed the underlying random number generator. This value should + /// have at least 128 bits of entropy. + static int random_seed (const char * seed); + + /// Set the Entropy Gathering Daemon (EGD) UNIX domain socket file to + /// read random seed values from. + static int egd_file (const char * socket_file); + + /** + * Set the file that contains the random seed value state, and the + * amount of bytes to read. "-1" bytes causes the entire file to be + * read. + */ + static int seed_file (const char * seed_file, long bytes = -1); + //@} + + /// Print SSL error corresponding to the given error code. + static void report_error (unsigned long error_code); + + /// Print the last SSL error for the current thread. + static void report_error (void); + + /** + * @name Diffie-Hellman (DH) Parameters + * + * When using DSS-based certificates, Diffie-Hellman keys need to be + * exchanged. These must be provided in the form of DH key + * generation parameters loaded in, or as fixed keys hardcoded into + * the code itself. ACE_SSL supports loaded parameters. + * + */ + //@{ + /** + * Load Diffie-Hellman parameters from file_name. The specified file can be + * a standalone file containing only DH parameters (e.g., as created + * by <code>openssl dhparam</code>), or it can be a certificate which has + * a PEM-encoded set of DH params concatenated on to i. + */ + int dh_params (const char *file_name, int type = SSL_FILETYPE_PEM); + const char *dh_params_file_name () const; + int dh_params_file_type () const; + //@} + +private: + + /// Verify if the context has been initialized or not. + void check_context (void); + + /// @@ More to document + void ssl_library_init (); + void ssl_library_fini (); + + // = Prevent assignment and copy initialization. + //@{ + ACE_SSL_Context (const ACE_SSL_Context &); + ACE_SSL_Context & operator= (const ACE_SSL_Context &); + //@} + +private: + + /// The SSL_CTX structure + SSL_CTX *context_; + + /// Cache the mode so we can answer fast + int mode_; + + /// The private key, certificate, and Diffie-Hellman paramters files + ACE_SSL_Data_File private_key_; + ACE_SSL_Data_File certificate_; + ACE_SSL_Data_File dh_params_; + + /// The default verify mode. + int default_verify_mode_; + + /// count of successful CA load attempts + int have_ca_; + +#ifdef ACE_HAS_THREADS + /// Array of mutexes used internally by OpenSSL when the SSL + /// application is multithreaded. + static lock_type * locks_; +#endif /* ACE_HAS_THREADS */ + +}; + +ACE_END_VERSIONED_NAMESPACE_DECL + +#if defined(__ACE_INLINE__) +#include "SSL_Context.inl" +#endif /* __ACE_INLINE__ */ + +#include /**/ "ace/post.h" + +#endif /* ACE_SSL_CONTEXT_H */ diff --git a/ACE/ace/SSL/SSL_Context.inl b/ACE/ace/SSL/SSL_Context.inl new file mode 100644 index 00000000000..9962ad09bdc --- /dev/null +++ b/ACE/ace/SSL/SSL_Context.inl @@ -0,0 +1,113 @@ +// -*- C++ -*- +// +// $Id$ + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_INLINE +ACE_SSL_Data_File::ACE_SSL_Data_File (void) + : type_ (-1) +{ +} + +ACE_INLINE +ACE_SSL_Data_File::ACE_SSL_Data_File (const char *file_name, + int type) + : file_name_ (file_name), + type_ (type) +{ +} + +ACE_INLINE const char * +ACE_SSL_Data_File::file_name (void) const +{ + return this->file_name_.c_str (); +} + +ACE_INLINE int +ACE_SSL_Data_File::type (void) const +{ + return this->type_; +} + +// **************************************************************** + +ACE_INLINE void +ACE_SSL_Context::check_context (void) +{ + if (this->context_ == 0) + { + this->set_mode (); + } + + ::SSL_CTX_set_verify (this->context_, this->default_verify_mode (), 0); +} + +ACE_INLINE SSL_CTX * +ACE_SSL_Context::context (void) +{ + this->check_context (); + return this->context_; +} + +ACE_INLINE int +ACE_SSL_Context::private_key_type (void) const +{ + return this->private_key_.type (); +} + +ACE_INLINE const char* +ACE_SSL_Context::private_key_file_name (void) const +{ + return this->private_key_.file_name (); +} + +ACE_INLINE int +ACE_SSL_Context::certificate_type (void) const +{ + return this->certificate_.type (); +} + +ACE_INLINE const char* +ACE_SSL_Context::certificate_file_name (void) const +{ + return this->certificate_.file_name (); +} + +ACE_INLINE int +ACE_SSL_Context::dh_params_file_type (void) const +{ + return this->dh_params_.type (); +} + +ACE_INLINE const char* +ACE_SSL_Context::dh_params_file_name (void) const +{ + return this->dh_params_.file_name (); +} + +ACE_INLINE void +ACE_SSL_Context::default_verify_mode (int mode) +{ + this->default_verify_mode_ = mode; +} + +ACE_INLINE int +ACE_SSL_Context::default_verify_mode (void) const +{ + return this->default_verify_mode_; +} + +ACE_INLINE int +ACE_SSL_Context::get_mode (void) const +{ + return this->mode_; +} + +ACE_INLINE int +ACE_SSL_Context::have_trusted_ca (void) const +{ + return this->have_ca_; +} + +ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SSL/SSL_Export.h b/ACE/ace/SSL/SSL_Export.h new file mode 100644 index 00000000000..542ec536f5a --- /dev/null +++ b/ACE/ace/SSL/SSL_Export.h @@ -0,0 +1,44 @@ +// -*- C++ -*- +// $Id$ +// Definition for Win32 Export directives. +// This file is generated automatically by +// generate_export_file.pl +// ------------------------------ +#if !defined (ACE_SSL_EXPORT_H) +#define ACE_SSL_EXPORT_H + +#include /**/ "ace/config-all.h" + +#if defined (ACE_AS_STATIC_LIBS) && !defined (ACE_SSL_HAS_DLL) +# define ACE_SSL_HAS_DLL 0 +#endif /* ACE_AS_STATIC_LIBS && ACE_SSL_HAS_DLL */ + +#if !defined (ACE_SSL_HAS_DLL) +#define ACE_SSL_HAS_DLL 1 +#endif /* ! ACE_SSL_HAS_DLL */ + +#if defined (ACE_SSL_HAS_DLL) +# if (ACE_SSL_HAS_DLL == 1) +# if defined (ACE_SSL_BUILD_DLL) +# define ACE_SSL_Export ACE_Proper_Export_Flag +# define ACE_SSL_SINGLETON_DECLARATION(T) ACE_EXPORT_SINGLETON_DECLARATION (T) +# define ACE_SSL_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) ACE_EXPORT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# else +# define ACE_SSL_Export ACE_Proper_Import_Flag +# define ACE_SSL_SINGLETON_DECLARATION(T) ACE_IMPORT_SINGLETON_DECLARATION (T) +# define ACE_SSL_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) ACE_IMPORT_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# endif /* ACE_SSL_BUILD_DLL */ +# else +# define ACE_SSL_Export +# define ACE_SSL_SINGLETON_DECLARATION(T) +# define ACE_SSL_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +# endif /* ! ACE_SSL_HAS_DLL == 1 */ +#else +# define ACE_SSL_Export +# define ACE_SSL_SINGLETON_DECLARATION(T) +# define ACE_SSL_SINGLETON_DECLARE(SINGLETON_TYPE, CLASS, LOCK) +#endif /* ACE_SSL_HAS_DLL */ + +#endif /* ACE_SSL_EXPORT_H */ + +// End of auto generated file. diff --git a/ACE/ace/SSL/SSL_SOCK.cpp b/ACE/ace/SSL/SSL_SOCK.cpp new file mode 100644 index 00000000000..fdcc95ddd11 --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK.cpp @@ -0,0 +1,72 @@ +// $Id$ + +#include "SSL_SOCK.h" + +#if !defined (__ACE_INLINE__) +#include "SSL_SOCK.inl" +#endif /* __ACE_INLINE__ */ + +#include "ace/OS_NS_errno.h" +#include "ace/os_include/os_signal.h" + +ACE_RCSID (ACE_SSL, + SSL_SOCK, + "$Id$") + + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_SSL_SOCK::ACE_SSL_SOCK (void) +{ + ACE_TRACE ("ACE_SSL_SOCK::ACE_SSL_SOCK"); +} + +ACE_SSL_SOCK::~ACE_SSL_SOCK (void) +{ + ACE_TRACE ("ACE_SSL_SOCK::~ACE_SSL_SOCK"); +} + +int +ACE_SSL_SOCK::enable (int value) const +{ + ACE_TRACE ("ACE_SSL_SOCK::enable"); + + switch (value) + { +#ifdef SIGURG + case SIGURG: + case ACE_SIGURG: +#endif /* SIGURG */ + case SIGIO: + case ACE_SIGIO: + case ACE_CLOEXEC: + ACE_NOTSUP_RETURN (-1); + case ACE_NONBLOCK: + return ACE_IPC_SAP::enable (value); + default: + return -1; + } +} + +int +ACE_SSL_SOCK::disable (int value) const +{ + ACE_TRACE("ACE_SSL_SOCK::disable"); + switch (value) + { +#ifdef SIGURG + case SIGURG: + case ACE_SIGURG: +#endif /* SIGURG */ + case SIGIO: + case ACE_SIGIO: + case ACE_CLOEXEC: + ACE_NOTSUP_RETURN (-1); + case ACE_NONBLOCK: + return ACE_IPC_SAP::disable (value); + default: + return -1; + } +} + +ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SSL/SSL_SOCK.h b/ACE/ace/SSL/SSL_SOCK.h new file mode 100644 index 00000000000..ad24db3a3cd --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK.h @@ -0,0 +1,103 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file SSL_SOCK.h + * + * $Id$ + * + * @author Ossama Othman <ossama@ece.uci.edu> + */ +//============================================================================= + + +#ifndef ACE_SSL_SOCK_H +#define ACE_SSL_SOCK_H + +#include /**/ "ace/pre.h" + +#include "SSL_Export.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/SOCK.h" + +#if defined (ACE_HAS_TEMPLATE_TYPEDEFS) +# define ACE_SSL_SOCK_ACCEPTOR ACE_SSL_SOCK_Acceptor +# define ACE_SSL_SOCK_CONNECTOR ACE_SSL_SOCK_Connector +# define ACE_SSL_SOCK_STREAM ACE_SSL_SOCK_Stream +#else +# define ACE_SSL_SOCK_ACCEPTOR ACE_SSL_SOCK_Acceptor, ACE_INET_Addr +# define ACE_SSL_SOCK_CONNECTOR ACE_SSL_SOCK_Connector, ACE_INET_Addr +# define ACE_SSL_SOCK_STREAM ACE_SSL_SOCK_Stream, ACE_INET_Addr +#endif /* ACE_HAS_TEMPLATE_TYPEDEFS */ + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +/** + * @class ACE_SSL_SOCK + * + * @brief An abstract class that forms the basis for more specific + * classes, such as "ACE_SSL_SOCK_Acceptor" and + * "ACE_SSL_SOCK_Stream". Do not instantiate this class. + * + * This class provides functions that are common to all of the + * ACE_SSL_SOCK_* classes. ACE_SSL_SOCK provides the ability + * to get and set socket options, get the local and remote + * addresses, and close the socket. + */ +class ACE_SSL_Export ACE_SSL_SOCK : public ACE_SOCK +{ +public: + + /** + * Override ACE_SOCK base class implementations with these SSL + * specific ones. + */ + //@{ + int set_option (int level, + int option, + void *optval, + int optlen) const; + int get_option (int level, + int option, + void *optval, + int *optlen) const; + int enable (int value) const; + int disable (int value) const; + void set_handle (ACE_HANDLE); + ACE_HANDLE get_handle (void) const; + int control (int cmd, void *arg) const; + //@} + +protected: + + /// Default constructor is private to prevent instances of this class + /// from being defined. + ACE_SSL_SOCK (void); + + /// Destructor + /** + * Not a virtual destructor. Protected destructor to prevent + * operator delete() from being called through a base class + * ACE_SSL_SOCK pointer/reference. + */ + ~ACE_SSL_SOCK (void); + +}; + +ACE_END_VERSIONED_NAMESPACE_DECL + +#if defined (__ACE_INLINE__) +#include "SSL_SOCK.inl" +#endif /* __ACE_INLINE__ */ + +#include /**/ "ace/post.h" + +#endif /* ACE_SSL_SOCK_H */ + + + + diff --git a/ACE/ace/SSL/SSL_SOCK.inl b/ACE/ace/SSL/SSL_SOCK.inl new file mode 100644 index 00000000000..1a39d5214dc --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK.inl @@ -0,0 +1,71 @@ +// -*- C++ -*- +// +// $Id$ + +#include "ace/OS_NS_sys_socket.h" + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_INLINE void +ACE_SSL_SOCK::set_handle (ACE_HANDLE fd) +{ + this->ACE_SOCK::set_handle (fd); +} + +ACE_INLINE ACE_HANDLE +ACE_SSL_SOCK::get_handle (void) const +{ + // return this->ssl_ ? (ACE_HANDLE) ::SSL_get_fd (this->ssl_) : ACE_INVALID_HANDLE; + return this->ACE_SOCK::get_handle (); +} + + +ACE_INLINE int +ACE_SSL_SOCK::control (int cmd, void *arg) const +{ + return ACE_OS::ioctl (this->get_handle (), cmd, arg); +} + +ACE_INLINE int +ACE_SSL_SOCK::set_option (int level, + int option, + void *optval, + int optlen) const +{ +// switch (option) +// { +// case SO_SNDBUF: +// return ::BIO_set_write_buffer_size (this->io_bio_, *((int *) optval)); +// case SO_RCVCBUF: +// return ::BIO_set_read_buffer_size (this->io_bio_, *((int *) optval)); +// default: + return ACE_OS::setsockopt (this->get_handle (), + level, + option, (char *) optval, + optlen); +// } +} + +// Provides access to the ACE_OS::getsockopt system call. + +ACE_INLINE int +ACE_SSL_SOCK::get_option (int level, + int option, + void *optval, + int *optlen) const +{ +// switch (option) +// { +// case SO_SNDBUF: +// return ::BIO_get_write_buffer_size (this->io_bio_, *((int *) optval)); +// case SO_RCVCBUF: +// return ::BIO_get_read_buffer_size (this->io_bio_, *((int *) optval)); +// default: + return ACE_OS::getsockopt (this->get_handle (), + level, + option, (char *) optval, + optlen); +// } +} + +ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SSL/SSL_SOCK_Acceptor.cpp b/ACE/ace/SSL/SSL_SOCK_Acceptor.cpp new file mode 100644 index 00000000000..80b62b5c1ce --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK_Acceptor.cpp @@ -0,0 +1,249 @@ +// -*- C++ -*- +// +// $Id$ + + +#include "SSL_SOCK_Acceptor.h" + +#include "ace/Handle_Set.h" +#include "ace/OS_Errno.h" +#include "ace/OS_NS_errno.h" +#include "ace/Log_Msg.h" +#include "ace/Time_Value.h" +#include "ace/Countdown_Time.h" + +#if !defined (__ACE_INLINE__) +#include "SSL_SOCK_Acceptor.inl" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID (ACE_SSL, + SSL_SOCK_Acceptor, + "$Id$") + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_ALLOC_HOOK_DEFINE(ACE_SSL_SOCK_Acceptor) + +ACE_SSL_SOCK_Acceptor::~ACE_SSL_SOCK_Acceptor (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Acceptor::~ACE_SSL_SOCK_Acceptor"); +} + + +int +ACE_SSL_SOCK_Acceptor::ssl_accept (ACE_SSL_SOCK_Stream &new_stream, + ACE_Time_Value *timeout) const +{ + SSL *ssl = new_stream.ssl (); + + if (SSL_is_init_finished (ssl)) + return 0; + + if (!SSL_in_accept_init (ssl)) + ::SSL_set_accept_state (ssl); + + ACE_HANDLE handle = new_stream.get_handle (); + + // We're going to call SSL_accept, optionally doing ACE::select and + // retrying the SSL_accept, until the SSL handshake is done or + // it fails. + // To get the timeout affect, set the socket to nonblocking mode + // before beginning if there is a timeout specified. If the timeout + // is 0 (wait as long as it takes) then don't worry about the blocking + // status; we'll block in SSL_accept if the socket is blocking, and + // block in ACE::select if not. + int reset_blocking_mode = 0; + if (timeout != 0) + { + reset_blocking_mode = ACE_BIT_DISABLED (ACE::get_flags (handle), + ACE_NONBLOCK); + // Set the handle into non-blocking mode if it's not already + // in it. + if (reset_blocking_mode + && ACE::set_flags (handle, + ACE_NONBLOCK) == -1) + return -1; + } + + // Take into account the time between each select() call below. + ACE_Countdown_Time countdown (timeout); + + int status; + do + { + // These handle sets are used to set up for whatever SSL_accept + // says it wants next. They're reset on each pass around the loop. + ACE_Handle_Set rd_handle; + ACE_Handle_Set wr_handle; + + status = ::SSL_accept (ssl); + switch (::SSL_get_error (ssl, status)) + { + case SSL_ERROR_NONE: + status = 0; // To tell caller about success + break; // Done + + case SSL_ERROR_WANT_WRITE: + wr_handle.set_bit (handle); + status = 1; // Wait for more activity + break; + + case SSL_ERROR_WANT_READ: + rd_handle.set_bit (handle); + status = 1; // Wait for more activity + break; + + case SSL_ERROR_ZERO_RETURN: + // The peer has notified us that it is shutting down via + // the SSL "close_notify" message so we need to + // shutdown, too. + status = -1; + break; + + case SSL_ERROR_SYSCALL: + // On some platforms (e.g. MS Windows) OpenSSL does not + // store the last error in errno so explicitly do so. + // + // Explicitly check for EWOULDBLOCK since it doesn't get + // converted to an SSL_ERROR_WANT_{READ,WRITE} on some + // platforms. If SSL_accept failed outright, though, don't + // bother checking more. This can happen if the socket gets + // closed during the handshake. + if (ACE_OS::set_errno_to_last_error () == EWOULDBLOCK && + status == -1) + { + // Although the SSL_ERROR_WANT_READ/WRITE isn't getting + // set correctly, the read/write state should be valid. + // Use that to decide what to do. + status = 1; // Wait for more activity + if (SSL_want_write (ssl)) + wr_handle.set_bit (handle); + else if (SSL_want_read (ssl)) + rd_handle.set_bit (handle); + else + status = -1; // Doesn't want anything - bail out + } + else + status = -1; + break; + + default: + ACE_SSL_Context::report_error (); + status = -1; + break; + } + + if (status == 1) + { + // Must have at least one handle to wait for at this point. + ACE_ASSERT (rd_handle.num_set() == 1 || wr_handle.num_set () == 1); + status = ACE::select (int (handle) + 1, + &rd_handle, + &wr_handle, + 0, + timeout); + + (void) countdown.update (); + + // 0 is timeout, so we're done. + // -1 is error, so we're done. + // Could be both handles set (same handle in both masks) so + // set to 1. + if (status >= 1) + status = 1; + else // Timeout or failure + status = -1; + } + + } while (status == 1 && !SSL_is_init_finished (ssl)); + + if (reset_blocking_mode) + { + ACE_Errno_Guard eguard (errno); + ACE::clr_flags (handle, ACE_NONBLOCK); + } + + return (status == -1 ? -1 : 0); + +} + +// General purpose routine for accepting new connections. +// Since our underlying acceptor is of the plain old ACE_SOCK_Acceptor +// variety, get the basic socket setup done with it, then take care of +// the SSL handshake if the socket is accepted. +int +ACE_SSL_SOCK_Acceptor::accept (ACE_SSL_SOCK_Stream &new_stream, + ACE_Addr *remote_addr, + ACE_Time_Value *timeout, + int restart, + int reset_new_handle) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Acceptor::accept"); + + // Take into account the time to complete the basic TCP handshake + // and the SSL handshake. + ACE_Countdown_Time countdown (timeout); + + ACE_SOCK_Stream temp_stream; + if (-1 == this->acceptor_.accept (temp_stream, + remote_addr, + timeout, + restart, + reset_new_handle)) + return -1; + + (void) countdown.update (); + + new_stream.set_handle (temp_stream.get_handle ()); + temp_stream.set_handle (ACE_INVALID_HANDLE); + + if (this->ssl_accept (new_stream, timeout) == -1) + { + new_stream.close (); + new_stream.set_handle (ACE_INVALID_HANDLE); + return -1; + } + + return 0; + +} + +int +ACE_SSL_SOCK_Acceptor::accept (ACE_SSL_SOCK_Stream &new_stream, + ACE_Accept_QoS_Params qos_params, + ACE_Addr *remote_addr, + ACE_Time_Value *timeout, + int restart, + int reset_new_handle) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Acceptor::accept"); + + // Take into account the time to complete the basic TCP handshake + // and the SSL handshake. + ACE_Countdown_Time countdown (timeout); + + ACE_SOCK_Stream temp_stream; + if (-1 == this->acceptor_.accept (temp_stream, + qos_params, + remote_addr, + timeout, + restart, + reset_new_handle)) + return -1; + + (void) countdown.update (); + + new_stream.set_handle (temp_stream.get_handle ()); + temp_stream.set_handle (ACE_INVALID_HANDLE); + + if (this->ssl_accept (new_stream, timeout) == -1) + { + new_stream.close (); + new_stream.set_handle (ACE_INVALID_HANDLE); + return -1; + } + + return 0; +} + +ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SSL/SSL_SOCK_Acceptor.h b/ACE/ace/SSL/SSL_SOCK_Acceptor.h new file mode 100644 index 00000000000..0c4609c2c73 --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK_Acceptor.h @@ -0,0 +1,198 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file SSL_SOCK_Acceptor.h + * + * $Id$ + * + * @author John Heitmann + * @author Chris Zimman + * @author Ossama Othman <ossama@uci.edu> + */ +//============================================================================= + + +#ifndef ACE_SSL_SOCK_ACCEPTOR_H +#define ACE_SSL_SOCK_ACCEPTOR_H + +#include /**/ "ace/pre.h" + +#include "SSL_Export.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "SSL_SOCK_Stream.h" + +#include "ace/SOCK_Acceptor.h" +#include "ace/OS_QoS.h" + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +/** + * @class ACE_SSL_SOCK_Acceptor + * + * @brief Defines a factory that creates new @c ACE_SSL_SOCK_Stream + * objects passively. + * + * The ACE_SSL_SOCK_Acceptor has its own @c ACE_SOCK_Acceptor + * which handles the basic socket acceptance. This class is a + * wrapper which adds the SSL acceptance handshake handling. + * Since SSL is record oriented, some additional steps must be taken + * after the basic socket acceptance to complete the SSL handshake that + * takes place at session establishment. + * + * @note The user must currently ensure that only one thread services + * a given SSL session at any given time since some underlying + * SSL implementations, such as OpenSSL, are not entirely + * thread-safe or reentrant. + */ +class ACE_SSL_Export ACE_SSL_SOCK_Acceptor : public ACE_SSL_SOCK +{ +public: + + /// Default constructor. + ACE_SSL_SOCK_Acceptor (void); + + /// Default destructor. + ~ACE_SSL_SOCK_Acceptor (void); + + /** + * Initiate a passive mode SSL/BSD-style acceptor socket. + * @param local_sap The address that we're going to listen for + * connections on. If this is @c ACE_Addr::sap_any, + * this socket listens on an the "any" IP address + * and selects an unused port. To find out what port + * was selected, call this object's + * @c ACE_SOCK::get_local_addr(ACE_Addr&) method + * upon return. + */ + ACE_SSL_SOCK_Acceptor (const ACE_Addr &local_sap, + int reuse_addr = 0, + int protocol_family = PF_UNSPEC, + int backlog = ACE_DEFAULT_BACKLOG, + int protocol = 0); + + /** + * Initiate a passive-mode QoS-enabled acceptor socket. + * @param local_sap The address that we're going to listen for + * connections on. If this is @c ACE_Addr::sap_any, + * this socket listens on an the "any" IP address + * and selects an unused port. To find out what port + * was selected, call this object's + * @c ACE_SOCK::get_local_addr(ACE_Addr&) method + * upon return. + */ + ACE_SSL_SOCK_Acceptor (const ACE_Addr &local_sap, + ACE_Protocol_Info *protocolinfo, + ACE_SOCK_GROUP g, + u_long flags, + int reuse_addr, + int protocol_family = PF_UNSPEC, + int backlog = ACE_DEFAULT_BACKLOG, + int protocol = 0); + + /** + * Initiate a passive mode SSL/BSD-style acceptor socket. + * @param local_sap The address that we're going to listen for + * connections on. If this is @c ACE_Addr::sap_any, + * this socket listens on an the "any" IP address + * and selects an unused port. To find out what port + * was selected, call this object's + * @c ACE_SOCK::get_local_addr(ACE_Addr&) method + * upon return. + * + * @return 0 if success; -1 for failure (errno contains error code). + */ + int open (const ACE_Addr &local_sap, + int reuse_addr = 0, + int protocol_family = PF_UNSPEC, + int backlog = ACE_DEFAULT_BACKLOG, + int protocol = 0); + + /// Close the listening socket. + int close (void); + + /** + * @name Passive Connection "accept" Methods + * + * These are the canonical methods exposed by the Acceptor pattern. + */ + //@{ + /** + * Accept a new ACE_SSL_SOCK_Stream connection. On successful return, + * the socket has been accepted and the SSL handshake has been completed. + * @param new_stream The @c ACE_SSL_SOCK_Stream object that will receive + * the new SSL socket. + * @param remote_addr Pointer to an @c ACE_INET_Addr object that will + * receive the address of the peer that connected. + * @param timeout The maximum time to wait for the combined socket + * acceptance and handshake completion. 0 means + * block forever, a timeout of {0, 0} means poll. + * @param restart 1 means "restart if interrupted," that is, + * if errno == EINTR. + * + * @return 0 if success; -1 for failure (errno contains error code). + */ + int accept (ACE_SSL_SOCK_Stream &new_stream, + ACE_Addr *remote_addr = 0, + ACE_Time_Value *timeout = 0, + int restart = 1, + int reset_new_handle = 0) const; + + /** + * Accept a new ACE_SSL_SOCK_Stream connection using the RVSP QoS + * information in qos_params. + * @param new_stream The @c ACE_SSL_SOCK_Stream object that will receive + * the new SSL socket. + * @param remote_addr Pointer to an @c ACE_INET_Addr object that will + * receive the address of the peer that connected. + * @param timeout The maximum time to wait for the combined socket + * acceptance and handshake completion. 0 means + * block forever, a timeout of {0, 0} means poll. + * @param restart 1 means "restart if interrupted," that is, + * if errno == EINTR. + * + * @return 0 if success; -1 for failure (errno contains error code). + */ + int accept (ACE_SSL_SOCK_Stream &new_stream, + ACE_Accept_QoS_Params qos_params, + ACE_Addr *remote_addr = 0, + ACE_Time_Value *timeout = 0, + int restart = 1, + int reset_new_handle = 0) const; + //@} + + /// Meta-type info + //@{ + typedef ACE_INET_Addr PEER_ADDR; + typedef ACE_SSL_SOCK_Stream PEER_STREAM; + //@} + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + + /// Complete SSL passive connection establishment. + int ssl_accept (ACE_SSL_SOCK_Stream &new_stream, + ACE_Time_Value *timeout) const; + +private: + + /// The BSD-socket workhorse + ACE_SOCK_Acceptor acceptor_; + +}; + +ACE_END_VERSIONED_NAMESPACE_DECL + +#if defined (__ACE_INLINE__) +#include "SSL_SOCK_Acceptor.inl" +#endif /* __ACE_INLINE__ */ + +#include /**/ "ace/post.h" + +#endif /* ACE_SSL_SOCK_ACCEPTOR_H */ diff --git a/ACE/ace/SSL/SSL_SOCK_Acceptor.inl b/ACE/ace/SSL/SSL_SOCK_Acceptor.inl new file mode 100644 index 00000000000..7fd8cb04095 --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK_Acceptor.inl @@ -0,0 +1,85 @@ +// -*- C++ -*- +// +// $Id$ + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_INLINE +ACE_SSL_SOCK_Acceptor::ACE_SSL_SOCK_Acceptor (void) + : acceptor_ () +{ + ACE_TRACE ("ACE_SSL_SOCK_Acceptor::ACE_SSL_SOCK_Acceptor"); +} + +ACE_INLINE +ACE_SSL_SOCK_Acceptor::ACE_SSL_SOCK_Acceptor (const ACE_Addr &local_sap, + int reuse_addr, + int protocol_family, + int backlog, + int protocol) + : acceptor_ (local_sap, + reuse_addr, + protocol_family, + backlog, + protocol) +{ + ACE_TRACE ("ACE_SSL_SOCK_Acceptor::ACE_SSL_SOCK_Acceptor"); + + this->set_handle (this->acceptor_.get_handle ()); +} + +ACE_INLINE +ACE_SSL_SOCK_Acceptor::ACE_SSL_SOCK_Acceptor (const ACE_Addr &local_sap, + ACE_Protocol_Info *protocolinfo, + ACE_SOCK_GROUP g, + u_long flags, + int reuse_addr, + int protocol_family, + int backlog, + int protocol) + : acceptor_ (local_sap, + protocolinfo, + g, + flags, + reuse_addr, + protocol_family, + backlog, + protocol) +{ + ACE_TRACE ("ACE_SSL_SOCK_Acceptor::ACE_SSL_SOCK_Acceptor"); + + this->set_handle (this->acceptor_.get_handle ()); +} + +ACE_INLINE int +ACE_SSL_SOCK_Acceptor::open (const ACE_Addr &local_sap, + int reuse_addr, + int protocol_family, + int backlog, + int protocol) +{ + ACE_TRACE ("ACE_SSL_SOCK_Acceptor::open"); + if (this->acceptor_.open (local_sap, + reuse_addr, + protocol_family, + backlog, + protocol) != 0) + return -1; + else + this->set_handle (this->acceptor_.get_handle ()); + + return 0; +} + +ACE_INLINE int +ACE_SSL_SOCK_Acceptor::close (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Acceptor::close ()"); + + int result = this->acceptor_.close (); + this->set_handle (ACE_INVALID_HANDLE); + + return result; +} + +ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SSL/SSL_SOCK_Connector.cpp b/ACE/ace/SSL/SSL_SOCK_Connector.cpp new file mode 100644 index 00000000000..0c6569e59ac --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK_Connector.cpp @@ -0,0 +1,411 @@ +// -*- C++ -*- +// +// $Id$ + +#include "SSL_SOCK_Connector.h" + +#include "ace/OS_NS_errno.h" +#include "ace/Handle_Set.h" +#include "ace/INET_Addr.h" +#include "ace/Log_Msg.h" +#include "ace/Countdown_Time.h" + +#include <openssl/err.h> + +#if !defined (__ACE_INLINE__) +#include "SSL_SOCK_Connector.inl" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID (ACE_SSL, + SSL_SOCK_Connector, + "$Id$") + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_ALLOC_HOOK_DEFINE(ACE_SSL_SOCK_Connector) + +ACE_SSL_SOCK_Connector::~ACE_SSL_SOCK_Connector (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Connector::~ACE_SSL_SOCK_Connector"); +} + +int +ACE_SSL_SOCK_Connector::ssl_connect (ACE_SSL_SOCK_Stream &new_stream, + const ACE_Time_Value *timeout) +{ + SSL *ssl = new_stream.ssl (); + + if (SSL_is_init_finished (ssl)) + return 0; + + // Check if a connection is already pending for the given SSL + // structure. + if (!SSL_in_connect_init (ssl)) + ::SSL_set_connect_state (ssl); + + ACE_HANDLE handle = new_stream.get_handle (); + + // We're going to call SSL_connect, optionally doing ACE::select and + // retrying the SSL_connect, until the SSL handshake is done or + // it fails. + // To get the timeout affect, set the socket to nonblocking mode + // before beginning if there is a timeout specified. If the timeout + // is 0 (wait as long as it takes) then don't worry about the blocking + // status; we'll block in SSL_connect if the socket is blocking, and + // block in ACE::select if not. + int reset_blocking_mode = 0; + if (timeout != 0) + { + reset_blocking_mode = ACE_BIT_DISABLED (ACE::get_flags (handle), + ACE_NONBLOCK); + // Set the handle into non-blocking mode if it's not already + // in it. + if (reset_blocking_mode + && ACE::set_flags (handle, + ACE_NONBLOCK) == -1) + return -1; + } + + ACE_Time_Value t; + if (timeout != 0) + t = *timeout; // Need a non-const copy. + + // Take into account the time between each select() call below. + ACE_Countdown_Time countdown ((timeout == 0 ? 0 : &t)); + + int status; + do + { + // These handle sets are used to set up for whatever SSL_connect + // says it wants next. They're reset on each pass around the loop. + ACE_Handle_Set rd_handle; + ACE_Handle_Set wr_handle; + + status = ::SSL_connect (ssl); + switch (::SSL_get_error (ssl, status)) + { + case SSL_ERROR_NONE: + // Start out with non-blocking disabled on the SSL stream. + new_stream.disable (ACE_NONBLOCK); + status = 0; // To tell caller about success + break; // Done + + case SSL_ERROR_WANT_WRITE: + wr_handle.set_bit (handle); + status = 1; // Wait for more activity + break; + + case SSL_ERROR_WANT_READ: + rd_handle.set_bit (handle); + status = 1; // Wait for more activity + break; + + case SSL_ERROR_ZERO_RETURN: + // The peer has notified us that it is shutting down via + // the SSL "close_notify" message so we need to + // shutdown, too. + status = -1; + break; + + case SSL_ERROR_SYSCALL: + // On some platforms (e.g. MS Windows) OpenSSL does not + // store the last error in errno so explicitly do so. + // + // Explicitly check for EWOULDBLOCK since it doesn't get + // converted to an SSL_ERROR_WANT_{READ,WRITE} on some + // platforms. If SSL_connect failed outright, though, don't + // bother checking more. This can happen if the socket gets + // closed during the handshake. + if (ACE_OS::set_errno_to_last_error () == EWOULDBLOCK && + status == -1) + { + // Although the SSL_ERROR_WANT_READ/WRITE isn't getting + // set correctly, the read/write state should be valid. + // Use that to decide what to do. + status = 1; // Wait for more activity + if (SSL_want_write (ssl)) + wr_handle.set_bit (handle); + else if (SSL_want_read (ssl)) + rd_handle.set_bit (handle); + else + status = -1; // Doesn't want anything - bail out + } + else + status = -1; + break; + + default: + ACE_SSL_Context::report_error (); + status = -1; + break; + } + + if (status == 1) + { + // Must have at least one handle to wait for at this point. + ACE_ASSERT (rd_handle.num_set () == 1 || wr_handle.num_set () == 1); + + // Block indefinitely if timeout pointer is zero. + status = ACE::select (int (handle) + 1, + &rd_handle, + &wr_handle, + 0, + (timeout == 0 ? 0 : &t)); + + (void) countdown.update (); + + // 0 is timeout, so we're done. + // -1 is error, so we're done. + // Could be both handles set (same handle in both masks) so set to 1. + if (status >= 1) + status = 1; + else // Timeout or socket failure + status = -1; + } + + } while (status == 1 && !SSL_is_init_finished (ssl)); + + if (reset_blocking_mode) + { + ACE_Errno_Guard eguard (errno); + ACE::clr_flags (handle, ACE_NONBLOCK); + } + + return (status == -1 ? -1 : 0); + +} + +int +ACE_SSL_SOCK_Connector::connect (ACE_SSL_SOCK_Stream &new_stream, + const ACE_Addr &remote_sap, + const ACE_Time_Value *timeout, + const ACE_Addr &local_sap, + int reuse_addr, + int flags, + int perms) +{ + ACE_TRACE ("ACE_SSL_SOCK_Connector::connect"); + + // Take into account the time to complete the basic TCP handshake + // and the SSL handshake. + ACE_Time_Value time_copy; + ACE_Countdown_Time countdown (&time_copy); + if (timeout != 0) + { + time_copy += *timeout; + countdown.start (); + } + + int result = + this->connector_.connect (new_stream.peer (), + remote_sap, + timeout, + local_sap, + reuse_addr, + flags, + perms); + + int error = 0; + if (result == -1) + error = errno; // Save us some TSS accesses. + + // Obtain the handle from the underlying SOCK_Stream and set it in + // the SSL_SOCK_Stream. Note that the case where a connection is in + // progress is also handled. In that case, the handle must also be + // set in the SSL_SOCK_Stream so that the correct handle is returned + // when performing non-blocking connect()s. + if (new_stream.get_handle () == ACE_INVALID_HANDLE + && (result == 0 + || (result == -1 && (error == EWOULDBLOCK + || error == EINPROGRESS)))) + new_stream.set_handle (new_stream.peer ().get_handle ()); + + if (result == -1) + return result; + + // If using a timeout, update the countdown timer to reflect the time + // spent on the connect itself, then pass the remaining time to + // ssl_connect to bound the time on the handshake. + if (timeout != 0) + { + countdown.update (); + timeout = &time_copy; + } + + result = this->ssl_connect (new_stream, timeout); + + if (result == -1) + new_stream.close (); + + return result; +} + +int +ACE_SSL_SOCK_Connector::connect (ACE_SSL_SOCK_Stream &new_stream, + const ACE_Addr &remote_sap, + ACE_QoS_Params qos_params, + const ACE_Time_Value *timeout, + const ACE_Addr &local_sap, + ACE_Protocol_Info *protocolinfo, + ACE_SOCK_GROUP g, + u_long flags, + int reuse_addr, + int perms) +{ + ACE_TRACE ("ACE_SSL_SOCK_Connector::connect"); + + // Take into account the time to complete the basic TCP handshake + // and the SSL handshake. + ACE_Time_Value time_copy; + ACE_Countdown_Time countdown (&time_copy); + if (timeout != 0) + { + time_copy += *timeout; + countdown.start (); + } + + int result = this->connector_.connect (new_stream.peer (), + remote_sap, + qos_params, + timeout, + local_sap, + protocolinfo, + g, + flags, + reuse_addr, + perms); + + int error = 0; + if (result == -1) + error = errno; // Save us some TSS accesses. + + // Obtain the handle from the underlying SOCK_Stream and set it in + // the SSL_SOCK_Stream. Note that the case where a connection is in + // progress is also handled. In that case, the handle must also be + // set in the SSL_SOCK_Stream so that the correct handle is returned + // when performing non-blocking connect()s. + if (new_stream.get_handle () == ACE_INVALID_HANDLE + && (result == 0 + || (result == -1 && (error == EWOULDBLOCK + || error == EINPROGRESS)))) + new_stream.set_handle (new_stream.peer ().get_handle ()); + + if (result == -1) + return result; + + // If using a timeout, update the countdown timer to reflect the time + // spent on the connect itself, then pass the remaining time to + // ssl_connect to bound the time on the handshake. + if (timeout != 0) + { + countdown.update (); + timeout = &time_copy; + } + + result = this->ssl_connect (new_stream, timeout); + + if (result == -1) + new_stream.close (); + + return result; +} + +// Try to complete a non-blocking connection. + +int +ACE_SSL_SOCK_Connector::complete (ACE_SSL_SOCK_Stream &new_stream, + ACE_Addr *remote_sap, + const ACE_Time_Value *tv) +{ + ACE_TRACE ("ACE_SSL_SOCK_Connector::complete"); + + // Take into account the time to complete the basic TCP handshake + // and the SSL handshake. + ACE_Time_Value time_copy; + ACE_Countdown_Time countdown (&time_copy); + if (tv != 0) + { + time_copy += *tv; + countdown.start (); + } + + // Only attempt to complete the TCP connection if it that hasn't + // already been done. + ACE_INET_Addr raddr; + if (new_stream.peer ().get_remote_addr (raddr) != 0 + && this->connector_.complete (new_stream.peer (), + remote_sap, + tv) == -1) + return -1; + + // The handle in the SSL_SOCK_Stream should have already been set in + // the connect() method. + + // If using a timeout, update the countdown timer to reflect the time + // spent on the connect itself, then pass the remaining time to + // ssl_connect to bound the time on the handshake. + if (tv != 0) + { + countdown.update (); + tv = &time_copy; + } + + if (this->ssl_connect (new_stream, tv) == -1) + { + new_stream.close (); + return -1; + } + + return 0; + +} + + +ACE_SSL_SOCK_Connector::ACE_SSL_SOCK_Connector ( + ACE_SSL_SOCK_Stream &new_stream, + const ACE_Addr &remote_sap, + const ACE_Time_Value *timeout, + const ACE_Addr &local_sap, + int reuse_addr, + int flags, + int perms) + : connector_ () +{ + ACE_TRACE ("ACE_SSL_SOCK_Connector::ACE_SSL_SOCK_Connector"); + this->connect (new_stream, + remote_sap, + timeout, + local_sap, + reuse_addr, + flags, + perms); +} + +ACE_SSL_SOCK_Connector::ACE_SSL_SOCK_Connector ( + ACE_SSL_SOCK_Stream &new_stream, + const ACE_Addr &remote_sap, + ACE_QoS_Params qos_params, + const ACE_Time_Value *timeout, + const ACE_Addr &local_sap, + ACE_Protocol_Info *protocolinfo, + ACE_SOCK_GROUP g, + u_long flags, + int reuse_addr, + int perms) + : connector_ () +{ + ACE_TRACE ("ACE_SSL_SOCK_Connector::ACE_SSL_SOCK_Connector"); + + this->connect (new_stream, + remote_sap, + qos_params, + timeout, + local_sap, + protocolinfo, + g, + flags, + reuse_addr, + perms); +} + +ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SSL/SSL_SOCK_Connector.h b/ACE/ace/SSL/SSL_SOCK_Connector.h new file mode 100644 index 00000000000..5419161cf4e --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK_Connector.h @@ -0,0 +1,318 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file SSL_SOCK_Connector.h + * + * $Id$ + * + * @author Ossama Othman <ossama@uci.edu> + * @author Carlos O'Ryan <coryan@uci.edu> + * @author John Heitmann + * @author Chris Zimman + */ +//============================================================================= + + +#ifndef ACE_SSL_SOCK_CONNECTOR_H +#define ACE_SSL_SOCK_CONNECTOR_H + +#include /**/ "ace/pre.h" + +#include "SSL_Export.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "SSL_SOCK_Stream.h" + +#include "ace/SOCK_Connector.h" +#include "ace/OS_QoS.h" + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +/** + * @class ACE_SSL_SOCK_Connector + * + * @brief Defines a factory that creates new <ACE_SSL_SOCK_Stream>s + * actively. + * + * The ACE_SSL_SOCK_Connector doesn't have a socket of its own, + * i.e., it simply "borrows" the one from the ACE_SSL_SOCK_Stream + * that's being connected. The reason for this is that the + * underlying socket API doesn't use a "factory" socket to connect + * "data-mode" sockets. Therefore, there's no need to inherit + * ACE_SSL_SOCK_Connector from ACE_SSL_SOCK. + * + * Since SSL is record-oriented, some additional work is done after + * the plain socket is connected. + * + * @note The user must currently ensure that only one thread services + * a given SSL session at any given time since some underlying + * SSL implementations, such as OpenSSL, are not entirely + * thread-safe or reentrant. + */ +class ACE_SSL_Export ACE_SSL_SOCK_Connector +{ + +public: + + /// Default constructor. + ACE_SSL_SOCK_Connector (void); + + /** + * Actively connect to a peer, producing a connected @c ACE_SSL_SOCK_Stream + * object if the connection succeeds. This method performs both the + * initial socket connect and the SSL handshake. + * + * @param new_stream The @c ACE_SSL_SOCK_Stream object that will be + * connected to the peer. + * @param remote_sap The address that we are trying to connect to. + * The protocol family of @c remote_sap is used for + * the connected socket. That is, if @c remote_sap + * contains an IPv6 address, a socket with family + * PF_INET6 will be used, else it will be PF_INET. + * @param timeout Pointer to an @c ACE_Time_Value object with amount + * of time to wait to connect. If the pointer is 0 + * then the call blocks until the connection attempt + * is complete, whether it succeeds or fails. If + * *timeout == {0, 0} then the connection is done + * using nonblocking mode. In this case, if the + * connection can't be made immediately, this method + * returns -1 and errno == EWOULDBLOCK. + * If *timeout > {0, 0} then this is the maximum amount + * of time to wait before timing out; if the specified + * amount of time passes before the connection is made, + * this method returns -1 and errno == ETIME. Note + * the difference between this case and when a blocking + * connect is attmpted that TCP times out - in the latter + * case, errno will be ETIMEDOUT. + * @param local_sap (optional) The local address to bind to. If it's + * the default value of @c ACE_Addr::sap_any then the + * OS will choose an unused port. + * @param reuse_addr (optional) If the value is 1, the local address + * (@c local_sap) is reused, even if it hasn't been + * cleaned up yet. + * @param flags Ignored. + * @param perms Ignored. + * + * @return Returns 0 if the connection succeeds. If it fails, + * -1 is returned and errno contains a specific error + * code. + */ + ACE_SSL_SOCK_Connector (ACE_SSL_SOCK_Stream &new_stream, + const ACE_Addr &remote_sap, + const ACE_Time_Value *timeout = 0, + const ACE_Addr &local_sap = ACE_Addr::sap_any, + int reuse_addr = 0, + int flags = 0, + int perms = 0); + + /** + * Actively connect to a peer, producing a connected @c ACE_SSL_SOCK_Stream + * object if the connection succeeds. This method performs both the + * initial socket connect and the SSL handshake. + * + * @param new_stream The @c ACE_SSL_SOCK_Stream object that will be + * connected to the peer. + * @param remote_sap The address that we are trying to connect to. + * The protocol family of @c remote_sap is used for + * the connected socket. That is, if @c remote_sap + * contains an IPv6 address, a socket with family + * PF_INET6 will be used, else it will be PF_INET. + * @param qos_params Contains QoS parameters that are passed to the + * IntServ (RSVP) and DiffServ protocols. + * @see ACE_QoS_Params. + * @param timeout Pointer to an @c ACE_Time_Value object with amount + * of time to wait to connect. If the pointer is 0 + * then the call blocks until the connection attempt + * is complete, whether it succeeds or fails. If + * *timeout == {0, 0} then the connection is done + * using nonblocking mode. In this case, if the + * connection can't be made immediately, this method + * returns -1 and errno == EWOULDBLOCK. + * If *timeout > {0, 0} then this is the maximum amount + * of time to wait before timing out; if the specified + * amount of time passes before the connection is made, + * this method returns -1 and errno == ETIME. Note + * the difference between this case and when a blocking + * connect is attmpted that TCP times out - in the latter + * case, errno will be ETIMEDOUT. + * @param local_sap (optional) The local address to bind to. If it's + * the default value of @c ACE_Addr::sap_any then the + * OS will choose an unused port. + * @param reuse_addr (optional) If the value is 1, the local address + * (@c local_sap) is reused, even if it hasn't been + * cleaned up yet. + * @param flags Ignored. + * @param perms Ignored. + * + * @return Returns 0 if the connection succeeds. If it fails, + * -1 is returned and errno contains a specific error + * code. + */ + ACE_SSL_SOCK_Connector (ACE_SSL_SOCK_Stream &new_stream, + const ACE_Addr &remote_sap, + ACE_QoS_Params qos_params, + const ACE_Time_Value *timeout = 0, + const ACE_Addr &local_sap = ACE_Addr::sap_any, + ACE_Protocol_Info *protocolinfo = 0, + ACE_SOCK_GROUP g = 0, + u_long flags = 0, + int reuse_addr = 0, + int perms = 0); + + /// Default dtor. + ~ACE_SSL_SOCK_Connector (void); + + /** + * Actively connect to a peer, producing a connected @c ACE_SSL_SOCK_Stream + * object if the connection succeeds. This method performs both the + * initial socket connect and the SSL handshake. + * + * @param new_stream The @c ACE_SSL_SOCK_Stream object that will be + * connected to the peer. + * @param remote_sap The address that we are trying to connect to. + * The protocol family of @c remote_sap is used for + * the connected socket. That is, if @c remote_sap + * contains an IPv6 address, a socket with family + * PF_INET6 will be used, else it will be PF_INET. + * @param timeout Pointer to an @c ACE_Time_Value object with amount + * of time to wait to connect. If the pointer is 0 + * then the call blocks until the connection attempt + * is complete, whether it succeeds or fails. If + * *timeout == {0, 0} then the connection is done + * using nonblocking mode. In this case, if the + * connection can't be made immediately, this method + * returns -1 and errno == EWOULDBLOCK. + * If *timeout > {0, 0} then this is the maximum amount + * of time to wait before timing out; if the specified + * amount of time passes before the connection is made, + * this method returns -1 and errno == ETIME. Note + * the difference between this case and when a blocking + * connect is attmpted that TCP times out - in the latter + * case, errno will be ETIMEDOUT. + * @param local_sap (optional) The local address to bind to. If it's + * the default value of @c ACE_Addr::sap_any then the + * OS will choose an unused port. + * @param reuse_addr (optional) If the value is 1, the local address + * (@c local_sap) is reused, even if it hasn't been + * cleaned up yet. + * @param flags Ignored. + * @param perms Ignored. + * + * @return Returns 0 if the connection succeeds. If it fails, + * -1 is returned and errno contains a specific error + * code. + */ + int connect (ACE_SSL_SOCK_Stream &new_stream, + const ACE_Addr &remote_sap, + const ACE_Time_Value *timeout = 0, + const ACE_Addr &local_sap = ACE_Addr::sap_any, + int reuse_addr = 0, + int flags = 0, + int perms = 0); + + /** + * Actively connect to a peer, producing a connected @c ACE_SSL_SOCK_Stream + * object if the connection succeeds. This method performs both the + * initial socket connect and the SSL handshake. + * + * @param new_stream The @c ACE_SSL_SOCK_Stream object that will be + * connected to the peer. + * @param remote_sap The address that we are trying to connect to. + * The protocol family of @c remote_sap is used for + * the connected socket. That is, if @c remote_sap + * contains an IPv6 address, a socket with family + * PF_INET6 will be used, else it will be PF_INET. + * @param qos_params Contains QoS parameters that are passed to the + * IntServ (RSVP) and DiffServ protocols. + * @see ACE_QoS_Params. + * @param timeout Pointer to an @c ACE_Time_Value object with amount + * of time to wait to connect. If the pointer is 0 + * then the call blocks until the connection attempt + * is complete, whether it succeeds or fails. If + * *timeout == {0, 0} then the connection is done + * using nonblocking mode. In this case, if the + * connection can't be made immediately, this method + * returns -1 and errno == EWOULDBLOCK. + * If *timeout > {0, 0} then this is the maximum amount + * of time to wait before timing out; if the specified + * amount of time passes before the connection is made, + * this method returns -1 and errno == ETIME. Note + * the difference between this case and when a blocking + * connect is attmpted that TCP times out - in the latter + * case, errno will be ETIMEDOUT. + * @param local_sap (optional) The local address to bind to. If it's + * the default value of @c ACE_Addr::sap_any then the + * OS will choose an unused port. + * @param reuse_addr (optional) If the value is 1, the local address + * (@c local_sap) is reused, even if it hasn't been + * cleaned up yet. + * @param flags Ignored. + * @param perms Ignored. + * + * @return Returns 0 if the connection succeeds. If it fails, + * -1 is returned and errno contains a specific error + * code. + */ + int connect (ACE_SSL_SOCK_Stream &new_stream, + const ACE_Addr &remote_sap, + ACE_QoS_Params qos_params, + const ACE_Time_Value *timeout = 0, + const ACE_Addr &local_sap = ACE_Addr::sap_any, + ACE_Protocol_Info *protocolinfo = 0, + ACE_SOCK_GROUP g = 0, + u_long flags = 0, + int reuse_addr = 0, + int perms = 0); + + /** + * Try to complete a non-blocking connection. + * If connection completion is successful then <new_stream> contains + * the connected ACE_SSL_SOCK_Stream. If <remote_sap> is non-NULL + * then it will contain the address of the connected peer. + */ + int complete (ACE_SSL_SOCK_Stream &new_stream, + ACE_Addr *remote_sap = 0, + const ACE_Time_Value *timeout = 0); + + /// Resets any event associations on this handle + int reset_new_handle (ACE_HANDLE handle); + + /// Meta-type info + //@{ + typedef ACE_INET_Addr PEER_ADDR; + typedef ACE_SSL_SOCK_Stream PEER_STREAM; + //@} + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + + /// Complete non-blocking SSL active connection. + int ssl_connect (ACE_SSL_SOCK_Stream &new_stream, + const ACE_Time_Value *timeout); + +private: + + /// The class that does all of the non-secure socket connection. + /// It is default contructed, and subsequently used by connect(). + ACE_SOCK_Connector connector_; + +}; + +ACE_END_VERSIONED_NAMESPACE_DECL + +#if defined (__ACE_INLINE__) +#include "SSL_SOCK_Connector.inl" +#endif /* __ACE_INLINE__ */ + +#include /**/ "ace/post.h" + +#endif /* ACE_SSL_SOCK_CONNECTOR_H */ diff --git a/ACE/ace/SSL/SSL_SOCK_Connector.inl b/ACE/ace/SSL/SSL_SOCK_Connector.inl new file mode 100644 index 00000000000..67b5ef01540 --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK_Connector.inl @@ -0,0 +1,28 @@ +// -*- C++ -*- +// +// $Id$ + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_INLINE +ACE_SSL_SOCK_Connector::ACE_SSL_SOCK_Connector (void) + : connector_ () +{ + ACE_TRACE ("ACE_SSL_SOCK_Connector::ACE_SSL_SOCK_Connector"); +} + +ACE_INLINE int +ACE_SSL_SOCK_Connector::reset_new_handle (ACE_HANDLE handle) +{ + ACE_TRACE ("ACE_SSL_SOCK_Connector::reset_new_handle"); + return this->connector_.reset_new_handle (handle); +} + +ACE_INLINE void +ACE_SSL_SOCK_Connector::dump (void) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Connector::dump"); + this->connector_.dump (); +} + +ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SSL/SSL_SOCK_Stream.cpp b/ACE/ace/SSL/SSL_SOCK_Stream.cpp new file mode 100644 index 00000000000..46130a79fc7 --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK_Stream.cpp @@ -0,0 +1,583 @@ +// $Id$ + +#include "ace/Handle_Set.h" +#include "ace/Log_Msg.h" +#include "ace/Countdown_Time.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_sys_select.h" +#include "ace/OS_Memory.h" + +#include <openssl/err.h> + +#include "SSL_SOCK_Stream.h" + +#if !defined (__ACE_INLINE__) +#include "SSL_SOCK_Stream.inl" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID (ACE_SSL, + SSL_SOCK_Stream, + "$Id$") + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_ALLOC_HOOK_DEFINE(ACE_SSL_SOCK_Stream) + +ACE_SSL_SOCK_Stream::ACE_SSL_SOCK_Stream (ACE_SSL_Context *context) + : ssl_ (0), + stream_ () +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::ACE_SSL_SOCK_Stream"); + + ACE_SSL_Context * ctx = + (context == 0 ? ACE_SSL_Context::instance () : context); + + this->ssl_ = ::SSL_new (ctx->context ()); + + if (this->ssl_ == 0) + { + ACE_ERROR ((LM_ERROR, + "(%P|%t) ACE_SSL_SOCK_Stream " + "- cannot allocate new SSL structure %p\n", + ACE_TEXT (""))); + } +} + +ACE_SSL_SOCK_Stream::~ACE_SSL_SOCK_Stream (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::~ACE_SSL_SOCK_Stream"); + + ::SSL_free (this->ssl_); + + // @@ Question: should we reference count the Context object or + // leave that to the application developer? We do not reference + // count reactors (for example) and following some simple rules + // seems to work fine! +} + +ssize_t +ACE_SSL_SOCK_Stream::sendv (const iovec iov[], + size_t n, + const ACE_Time_Value *max_wait_time) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::sendv"); + + // There is subtle problem in this method that occurs when using + // non-blocking IO. The semantics of a non-blocking scatter write + // (sendv()) are not possible to retain with the emulation in this + // method. + + ssize_t bytes_sent = 0; + + ACE_Time_Value t; + ACE_Time_Value *timeout = const_cast<ACE_Time_Value *> (max_wait_time); + + if (max_wait_time != 0) + { + // Make a copy since ACE_Countdown_Time modifies the + // ACE_Time_Value. + t = *max_wait_time; + timeout = &t; + } + + // Take into account the time between each send. + ACE_Countdown_Time countdown (timeout); + + for (size_t i = 0; i < n; ++i) + { + ssize_t const result = this->send (iov[i].iov_base, + iov[i].iov_len, + timeout); + + if (result == -1) + { + // There is a subtle difference in behaviour depending on + // whether or not any data was sent. If no data was sent, + // then always return -1. Otherwise return bytes_sent. + // This gives the caller an opportunity to keep track of + if (bytes_sent > 0) + break; + else + return -1; + } + else + { + bytes_sent += result; + + // Do not continue on to the next loop iteration if the + // amount of data sent was less than the amount data given. + // This avoids a subtle problem where "holes" in the data + // stream would occur if partial sends of a given buffer in + // the iovec array occured. + if (static_cast<size_t> (result) < static_cast<size_t> (iov[i].iov_len)) + break; + } + + (void) countdown.update (); + } + + return bytes_sent; +} + +ssize_t +ACE_SSL_SOCK_Stream::recvv (iovec *io_vec, + const ACE_Time_Value *timeout) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recvv"); + + // From ACE_SOCK_IO::recvv(). +#if defined (FIONREAD) + ACE_Handle_Set handle_set; + handle_set.reset (); + handle_set.set_bit (this->get_handle ()); + + io_vec->iov_base = 0; + + // Check the status of the current socket. + switch (ACE_OS::select (int (this->get_handle ()) + 1, + handle_set, + 0, 0, + timeout)) + { + case -1: + return -1; + /* NOTREACHED */ + case 0: + errno = ETIME; + return -1; + /* NOTREACHED */ + default: + // Goes fine, fallthrough to get data + break; + } + + int inlen; + + if (ACE_OS::ioctl (this->get_handle (), + FIONREAD, + &inlen) == -1) + return -1; + else if (inlen > 0) + { + ACE_NEW_RETURN (io_vec->iov_base, + char[inlen], + -1); + io_vec->iov_len = this->recv (io_vec->iov_base, + inlen); + return io_vec->iov_len; + } + else + return 0; +#else + ACE_UNUSED_ARG (io_vec); + ACE_UNUSED_ARG (timeout); + ACE_NOTSUP_RETURN (-1); +#endif /* FIONREAD */ +} + +ssize_t +ACE_SSL_SOCK_Stream::send (const void *buf, + size_t len, + int flags, + const ACE_Time_Value *timeout) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send"); + + // If SSL has data in the buffer, i.e. SSL_pending() returns a + // non-zero value, then don't block on select(). + if (timeout == 0 || ::SSL_pending (this->ssl_)) + return this->send (buf, len, flags); + + int val = 0; + if (ACE::enter_send_timedwait (this->get_handle (), + timeout, + val) == -1) + return -1; + + ssize_t const bytes_transferred = this->send (buf, len, flags); + + ACE::restore_non_blocking_mode (this->get_handle (), val); + + return bytes_transferred; +} + +ssize_t +ACE_SSL_SOCK_Stream::recv (void *buf, + size_t n, + int flags, + const ACE_Time_Value *timeout) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv"); + + return this->recv_i (buf, n, flags, timeout); +} + + +ssize_t +ACE_SSL_SOCK_Stream::send (size_t n, ...) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send"); + + size_t const total_tuples = n / 2; + + va_list argp; + va_start (argp, n); + + ssize_t bytes_sent = 0; + + // NOTE: This method used to fill an IO vector (e.g. iovec) and then + // send it using a scatter write (sendv()). However, it is + // not possible to emulate a non-blocking scatter write over + // SSL. As such, there is no point in attempting to use + // scatter writes over SSL. + for (size_t i = 0; i < total_tuples; ++i) + { + ssize_t const data_len = va_arg (argp, ssize_t); + ssize_t const result = this->send (va_arg (argp, char *), data_len); + + if (result == -1) + { + // There is a subtle difference in behaviour depending on + // whether or not any data was sent. If no data was sent, + // then always return -1. Otherwise return bytes_sent. + // This gives the caller an opportunity to keep track of + // which data was actually sent. + if (bytes_sent > 0) + break; + else + { + va_end (argp); + return -1; + } + } + else + { + bytes_sent += result; + + // Do not continue on to the next loop iteration if the + // amount of data sent was less than the amount of data + // given. This avoids a subtle problem where "holes" in the + // data stream would occur if partial sends of a given + // buffer in the varargs occured. + if (result < data_len) + break; + + } + } + + va_end (argp); + + return bytes_sent; +} + +ssize_t +ACE_SSL_SOCK_Stream::recv (size_t n, ...) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv"); + + size_t const total_tuples = n / 2; + + va_list argp; + va_start (argp, n); + + ssize_t bytes_recv = 0; + + for (size_t i = 0; i < total_tuples; ++i) + { + ssize_t const data_len = va_arg (argp, ssize_t); + ssize_t const result = this->recv (va_arg (argp, char *), data_len); + + if (result == -1) + { + // There is a subtle difference in behaviour depending on + // whether or not any data was received. If no data was + // received, then always return -1. Otherwise return + // bytes_received. This gives the caller an opportunity to + // keep track of which data was actually received. + if (bytes_recv > 0) + break; + else + { + va_end (argp); + return -1; + } + } + else + { + bytes_recv += result; + + // Do not continue on to the next loop iteration if the + // amount of data received was less than the amount of data + // desired. This avoids a subtle problem where "holes" in + // the data stream would occur if partial receives of a + // given buffer in the varargs occured. + if (result < data_len) + break; + + } + } + + va_end (argp); + + return bytes_recv; +} + +ssize_t +ACE_SSL_SOCK_Stream::send_n (const void *buf, + size_t len, + int flags, + const ACE_Time_Value *timeout, + size_t *bt) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send_n"); + + // No support for send flags in SSL. + if (flags != 0) + ACE_NOTSUP_RETURN (-1); + + /* This code mimics ACE::send_n */ + // Total number of bytes written. + size_t temp = 0; + size_t &bytes_transferred = ((bt == 0) ? temp : *bt); + + // Actual number of bytes written in each <send> attempt + ssize_t n = 0; + + for (bytes_transferred = 0; + bytes_transferred < len; + bytes_transferred += n) + { + n = this->send ((const char*) buf + bytes_transferred, + len - bytes_transferred, + flags, + timeout); + + if (n < 0) + { + if (errno == EWOULDBLOCK) + { + // If blocked, try again. + n = 0; + continue; + } + else + return -1; + } + else if (n == 0) + break; + } + + return bytes_transferred; +} + +ssize_t +ACE_SSL_SOCK_Stream::recv_n (void *buf, + size_t len, + int flags, + const ACE_Time_Value *timeout, + size_t *bt) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv_n"); + + if (flags != 0) + { + if ((flags | MSG_PEEK) != MSG_PEEK) + ACE_NOTSUP_RETURN (-1); + } + + size_t temp = 0; + size_t &bytes_transferred = ((bt == 0) ? temp : *bt); + + ssize_t n = 0; + + for (bytes_transferred = 0; + bytes_transferred < len; + bytes_transferred += n) + { + n = this->recv ((char*) buf + bytes_transferred, + len - bytes_transferred, + flags, + timeout); + + if (n < 0) + { + if (errno == EWOULDBLOCK) + { + // If blocked, try again. + n = 0; + continue; + } + else + return -1; + } + else if (n == 0) + break; + } + + return bytes_transferred; +} + +ssize_t +ACE_SSL_SOCK_Stream::recv_n (void *buf, int len, int flags) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv_n"); + + if (flags != 0) + { + if ((flags | MSG_PEEK) != MSG_PEEK) + ACE_NOTSUP_RETURN (-1); + } + + ssize_t bytes_transferred = 0; + ssize_t n = 0; + + for (bytes_transferred = 0; + bytes_transferred < len; + bytes_transferred += n) + { + n = this->recv ((char*) buf + bytes_transferred, + len - bytes_transferred, + flags); + + if (n < 0) + { + if (errno == EWOULDBLOCK) + { + // If blocked, try again. + n = 0; + continue; + } + else + return -1; + } + else if (n == 0) + break; + } + + return bytes_transferred; +} + +ssize_t +ACE_SSL_SOCK_Stream::send_n (const void *buf, int len, int flags) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send_n"); + + // Send flags are unsupported in SSL + if (flags != 0) + ACE_NOTSUP_RETURN (-1); + + /* The following code mimics <ACE::send_n> */ + size_t bytes_transferred = 0; + ssize_t n = 0; + + for (bytes_transferred = 0; + bytes_transferred < (size_t) len; + bytes_transferred += n) + { + n = this->send ((const char*) buf + bytes_transferred, + len - bytes_transferred, + flags); + + if (n < 0) + { + if (errno == EWOULDBLOCK) + { + // If blocked, try again. + n = 0; + continue; + } + else + return -1; + } + else if (n == 0) + break; + } + + return bytes_transferred; +} + +ssize_t +ACE_SSL_SOCK_Stream::sendv_n (const iovec iov[], size_t iovcnt) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::sendv_n"); + + ssize_t bytes_sent = 0; + + for (size_t i = 0; i < iovcnt; ++i) + { + ssize_t result = this->send_n (iov[i].iov_base, + iov[i].iov_len); + + + if (result == -1) + { + // There is a subtle difference in behaviour depending on + // whether or not any data was sent. If no data was sent, + // then always return -1. Otherwise return bytes_sent. + // This gives the caller an opportunity to keep track of + // which data was actually sent. + if (bytes_sent > 0) + break; + else + return -1; + } + else + bytes_sent += result; + } + + return bytes_sent; +} + +ssize_t +ACE_SSL_SOCK_Stream::recvv_n (iovec iov[], size_t iovcnt) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recvv_n"); + + ssize_t bytes_read = 0; + + for (size_t i = 0; i < iovcnt; ++i) + { + ssize_t const result = this->recv_n (iov[i].iov_base, + iov[i].iov_len); + + if (result == -1) + { + // There is a subtle difference in behaviour depending on + // whether or not any data was read. If no data was read, + // then always return -1. Otherwise return bytes_read. + // This gives the caller an opportunity to keep track of + // which data was actually read. + if (bytes_read > 0) + break; + else + return -1; + } + else + bytes_read += result; + } + + return bytes_read; +} + +int +ACE_SSL_SOCK_Stream::get_remote_addr (ACE_Addr &addr) const +{ + // Some applications use get_remote_addr() as a way of determining + // whether or not a connection has been established. In SSL's case, + // the remote addr will be available once the TCP handshake has been + // complete. Despite that fact, the SSL connection may not have + // been completed. In such a case, a successful return from + // get_remote_addr() would be misleading. + + if (SSL_is_init_finished (this->ssl_)) + return this->ACE_SSL_SOCK::get_remote_addr (addr); + + if (this->get_handle () == ACE_INVALID_HANDLE) + errno = EBADF; + else + errno = ENOTCONN; + + return -1; +} + +ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SSL/SSL_SOCK_Stream.h b/ACE/ace/SSL/SSL_SOCK_Stream.h new file mode 100644 index 00000000000..0690dce9839 --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK_Stream.h @@ -0,0 +1,321 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file SSL_SOCK_Stream.h + * + * $Id$ + * + * @author Ossama Othman <ossama@uci.edu> + * @author Carlos O'Ryan <coryan@uci.edu> + * @author John Heitmann + */ +//============================================================================= + + +#ifndef ACE_SSL_SOCK_STREAM_H +#define ACE_SSL_SOCK_STREAM_H + +#include /**/ "ace/pre.h" + +#include "SSL_Export.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +// This must be included before any <openssl> include on LynxOS +#include "ace/os_include/os_stdio.h" + +#include <openssl/err.h> + +#include "SSL_SOCK.h" +#include "SSL_Context.h" + +#include "ace/SOCK_Stream.h" + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +/** + * @class ACE_SSL_SOCK_Stream + * + * @brief Defines methods in the ACE_SSL_SOCK_Stream abstraction. + * + * This class encapsulates the methods and functionality necessary to + * send and receive data over TLS/SSL. + * @par + * Since SSL is record-oriented, some additional steps must be taken + * to make the ACE_SSL_SOCK_Stream interact properly with the + * Reactor (if one is used) when performing non-blocking IO. In + * particular, if ::SSL_pending (ssl), where "ssl" is a pointer to the + * SSL data structure returned from ACE_SSL_SOCK_Stream::ssl(), + * returns a non-zero value then the event handler that calls the IO + * methods in this class should return a value greater than zero to + * force the Reactor to invoke the event handler before polling for + * additional events (e.g. blocking on select()). + * + * @note The user must currently ensure that only one thread services + * a given SSL session at any given time since underlying SSL + * implementations, such as OpenSSL, are not entirely + * thread-safe or reentrant. + */ +class ACE_SSL_Export ACE_SSL_SOCK_Stream : public ACE_SSL_SOCK +{ +public: + + /// Constructor + /** + * @param context Pointer to @c ACE_SSL_Context instance containing + * the OpenSSL @c SSL data structure to be associated + * with this @c ACE_SSL_SOCK_Stream. The @c SSL data + * structure will be copied to make it at least + * logically independent of the supplied @a context. + */ + ACE_SSL_SOCK_Stream (ACE_SSL_Context *context = + ACE_SSL_Context::instance ()); + + /// Destructor + ~ACE_SSL_SOCK_Stream (void); + + /// Send an n byte buffer to the ssl socket using the semantics of + /// send(3n). + /** + * ACE_SSL supports no flags for sending at this time. + */ + ssize_t send (const void *buf, + size_t n, + int flags) const; + + /// Recv an n byte buffer from the ssl socket using the semantics of + /// recv(3n). + /** + * ACE_SSL supports MSG_PEEK, but no other flags at this time. + */ + ssize_t recv (void *buf, + size_t n, + int flags) const; + + /// Send an n byte buffer to the ssl socket using the semantics of + /// write(2). + ssize_t send (const void *buf, + size_t n) const; + + /// Recv an n byte buffer from the ssl socket using the semantics of + /// read(2). + ssize_t recv (void *buf, + size_t n) const; + + /// Send an iovec of size n to the ssl socket. + /** + * Note that it is not possible to perform a "scattered" write with + * the underlying OpenSSL implementation. As such, the expected + * semantics are not fully reproduced with this implementation. + */ + ssize_t sendv (const iovec iov[], + size_t n, + const ACE_Time_Value *timeout = 0) const; + + /** + * Allows a client to read from a socket without having to provide a + * buffer to read. This method determines how much data is in the + * socket, allocates a buffer of this size, reads in the data, and + * returns the number of bytes read. The caller is responsible for + * deleting the member in the iov_base field of io_vec using + * delete [] io_vec->iov_base. + */ + ssize_t recvv (iovec *io_vec, + const ACE_Time_Value *timeout = 0) const; + + /** + * Wait to timeout amount of time to send up to n bytes into buf + * (uses the send() call). If send() times out -1 is returned with + * errno == ETIME. If it succeeds the number of bytes sent is + * returned. No flags are supported. + */ + ssize_t send (const void *buf, + size_t n, + int flags, + const ACE_Time_Value *timeout) const; + + /** + * Wait up to timeout amount of time to receive up to n bytes into + * buf (uses the recv() call). If recv() times out -1 is returned + * with errno == ETIME. If it succeeds the number of bytes received + * is returned. MSG_PEEK is the only supported flag. + */ + ssize_t recv (void *buf, + size_t n, + int flags, + const ACE_Time_Value *timeout) const; + + /** + * Wait to to timeout amount of time to send up to n bytes into + * buf (uses the send() call). If send() times out + * a -1 is returned with errno == ETIME. If it succeeds the + * number of bytes sent is returned. + */ + ssize_t send (const void *buf, + size_t n, + const ACE_Time_Value *timeout) const; + + /** + * Wait up to timeout amount of time to receive up to n bytes + * into buf (uses the recv() call). If recv() times + * out a -1 is returned with @c errno == ETIME. If it succeeds the + * number of bytes received is returned. + */ + ssize_t recv (void *buf, + size_t n, + const ACE_Time_Value *timeout) const; + + /// Send n varargs messages to the connected ssl socket. + ssize_t send (size_t n, + ...) const; + + /// Recv n varargs messages to the connected ssl socket. + ssize_t recv (size_t n, + ...) const; + + /// Send n bytes, keep trying until n are sent. + ssize_t send_n (const void *buf, int n) const; + + /// Recv n bytes, keep trying until n are received. + ssize_t recv_n (void *buf, int n) const; + + /** + * @note In the following four methods, only MSG_PEEK is supported + * for recv_n(), and no flags are supported for send_n(). + */ + //@{ + /// Send n bytes, keep trying until n are sent. + ssize_t send_n (const void *buf, int n, int flags) const; + + /// Recv n bytes, keep trying until n are sent. + ssize_t recv_n (void *buf, int n, int flags) const; + + /** + * Try to send exactly len bytes into buf (uses the send() call). + * If send() blocks for longer than timeout the number of bytes + * actually sent is returned with errno == ETIME. If a timeout does + * not occur, send_n() return len (i.e., the number of bytes + * requested to be sent). + */ + ssize_t send_n (const void *buf, + size_t len, + int flags, + const ACE_Time_Value *timeout, + size_t *bytes_transferred = 0) const; + + /** + * Try to receive exactly len bytes into buf (uses the recv() call). + * The ACE_Time_Value indicates how long to blocking trying to + * receive. If timeout == 0, the caller will block until action is + * possible, else will wait until the relative time specified in + * timeout elapses). If recv() blocks for longer than timeout the + * number of bytes actually read is returned with errno == ETIME. + * If a timeout does not occur, recv_n return len (i.e., the number + * of bytes requested to be read). + */ + ssize_t recv_n (void *buf, + size_t len, + int flags, + const ACE_Time_Value *timeout, + size_t *bytes_transferred = 0) const; + //@} + + /** + * Send an iovec of size n to the connected socket. Will block + * until all bytes are sent or an error occurs. + */ + ssize_t sendv_n (const iovec iov[], + size_t n) const; + + /// Receive an iovec of size n to the connected socket. + ssize_t recvv_n (iovec iov[], + size_t n) const; + + + /** + * Selectively close endpoints. + */ + //@{ + /// Close down the reader. + int close_reader (void); + + /// Close down the writer. + int close_writer (void); + //@} + + ///Close down the socket. + int close (void); + + /// Meta-type info + typedef ACE_INET_Addr PEER_ADDR; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + + /// Overridden set_handle() method. + /** + * Only an ACE_SSL_SOCK_Acceptor or ACE_SSL_SOCK_Connector should + * access this method since some state in the underlying "ssl_" data + * structure is set during SSL connection establishment. + */ + void set_handle (ACE_HANDLE fd); + + /// Return a pointer to the underlying SSL structure. + SSL *ssl (void) const; + + /** + * Return the address of the remotely connected peer (if there is + * one), in the referenced <ACE_Addr>. Returns 0 if successful, else + * -1. + * + * @note If the TCP connection has been completed but the SSL + * connection has not been completed yet, -1 will be + * returned. + */ + int get_remote_addr (ACE_Addr &) const; + + /// Return the underlying ACE_SOCK_Stream which ACE_SSL runs atop of. + ACE_SOCK_Stream & peer (void); + +protected: + + /// Underlying send() helper method common to all public send() + /// methods. + ssize_t send_i (const void *buf, + size_t n, + int flags) const; + + /// Underlying send() helper method common to all public send() + /// methods. + ssize_t recv_i (void *buf, + size_t n, + int flags, + const ACE_Time_Value *timeout) const; + +private: + + ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_SSL_SOCK_Stream &)) + ACE_UNIMPLEMENTED_FUNC (ACE_SSL_SOCK_Stream (const ACE_SSL_SOCK_Stream &)) + +protected: + + /// The SSL session. + SSL *ssl_; + + /// The stream which works under the ssl connection. + ACE_SOCK_Stream stream_; + +}; + +ACE_END_VERSIONED_NAMESPACE_DECL + +#if defined (__ACE_INLINE__) +#include "SSL_SOCK_Stream.inl" +#endif /* __ACE_INLINE__ */ + +#include /**/ "ace/post.h" + +#endif /* ACE_SSL_SOCK_STREAM_H */ diff --git a/ACE/ace/SSL/SSL_SOCK_Stream.inl b/ACE/ace/SSL/SSL_SOCK_Stream.inl new file mode 100644 index 00000000000..f28b55997a5 --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK_Stream.inl @@ -0,0 +1,323 @@ +// -*- C++ -*- +// +// $Id$ + +#include "ace/OS_NS_errno.h" + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_INLINE void +ACE_SSL_SOCK_Stream::set_handle (ACE_HANDLE fd) +{ + if (this->ssl_ == 0 || fd == ACE_INVALID_HANDLE) + { + this->ACE_SSL_SOCK::set_handle (ACE_INVALID_HANDLE); + return; + } + else + { + (void) ::SSL_set_fd (this->ssl_, (int) fd); + this->ACE_SSL_SOCK::set_handle (fd); + this->stream_.set_handle (fd); + } +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send_i (const void *buf, + size_t n, + int flags) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send_i"); + + // NOTE: Caller must provide thread-synchronization. + + // No send flags are supported in SSL. + if (flags != 0) + ACE_NOTSUP_RETURN (-1); + + int const bytes_sent = ::SSL_write (this->ssl_, + static_cast<const char *> (buf), + n); + + switch (::SSL_get_error (this->ssl_, bytes_sent)) + { + case SSL_ERROR_NONE: + return bytes_sent; + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + errno = EWOULDBLOCK; + + return -1; + + case SSL_ERROR_ZERO_RETURN: + // The peer has notified us that it is shutting down via the SSL + // "close_notify" message so we need to shutdown, too. + (void) ::SSL_shutdown (this->ssl_); + + return bytes_sent; + + case SSL_ERROR_SYSCALL: + if (bytes_sent == 0) + // An EOF occured but the SSL "close_notify" message was not + // sent. This is a protocol error, but we ignore it. + return 0; + + // If not an EOF, then fall through to "default" case. + + // On some platforms (e.g. MS Windows) OpenSSL does not store + // the last error in errno so explicitly do so. + ACE_OS::set_errno_to_last_error (); + + break; + + default: + // Reset errno to prevent previous values (e.g. EWOULDBLOCK) + // from being associated with fatal SSL errors. + errno = 0; + + ACE_SSL_Context::report_error (); + + break; + } + + return -1; +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send (const void *buf, + size_t n, + int flags) const +{ + return this->send_i (buf, n, flags); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv_i (void *buf, + size_t n, + int flags, + const ACE_Time_Value *timeout) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv_i"); + + // NOTE: Caller must provide thread-synchronization. + + int bytes_read = 0; + ACE_HANDLE const handle = this->get_handle (); + + // Value for current I/O mode (blocking/non-blocking) + int val = 0; + + if (timeout != 0) + ACE::record_and_set_non_blocking_mode (handle, + val); + + // Only block on select() with a timeout if no data in the + // internal OpenSSL buffer is pending read completion for + // the same reasons stated above, i.e. all data must be read + // before blocking on select(). + if (timeout != 0 + && !::SSL_pending (this->ssl_)) + { + if (ACE::enter_recv_timedwait (handle, + timeout, + val) == -1) + return -1; + } + + if (flags) + { + if (ACE_BIT_ENABLED (flags, MSG_PEEK)) + bytes_read = ::SSL_peek (this->ssl_, + static_cast<char *> (buf), + n); + else + ACE_NOTSUP_RETURN (-1); + } + else + { + bytes_read = ::SSL_read (this->ssl_, + static_cast<char *> (buf), + n); + } + + int const status = ::SSL_get_error (this->ssl_, bytes_read); + switch (status) + { + case SSL_ERROR_NONE: + if (timeout != 0) + ACE::restore_non_blocking_mode (handle, val); + + return bytes_read; + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + errno = EWOULDBLOCK; + + return -1; + + case SSL_ERROR_ZERO_RETURN: + if (timeout != 0) + ACE::restore_non_blocking_mode (handle, val); + + // The peer has notified us that it is shutting down via the SSL + // "close_notify" message so we need to shutdown, too. + (void) ::SSL_shutdown (this->ssl_); + + return bytes_read; + + case SSL_ERROR_SYSCALL: + if (bytes_read == 0) + // An EOF occured but the SSL "close_notify" message was not + // sent. This is a protocol error, but we ignore it. + return 0; + + // If not an EOF, then fall through to "default" case. + + // On some platforms (e.g. MS Windows) OpenSSL does not store + // the last error in errno so explicitly do so. + ACE_OS::set_errno_to_last_error (); + + break; + + default: + // Reset errno to prevent previous values (e.g. EWOULDBLOCK) + // from being associated with a fatal SSL error. + errno = 0; + + ACE_SSL_Context::report_error (); + + break; + } + + return -1; +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv (void *buf, + size_t n, + int flags) const +{ + return this->recv_i (buf, n, flags, 0); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send (const void *buf, + size_t n) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send"); + + return this->send_i (buf, n, 0); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv (void *buf, + size_t n) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv"); + + return this->recv_i (buf, n, 0, 0); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send (const void *buf, + size_t len, + const ACE_Time_Value *timeout) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send"); + return this->send (buf, len, 0, timeout); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv (void *buf, + size_t n, + const ACE_Time_Value *timeout) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv"); + return this->recv (buf, n, 0, timeout); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv_n (void *buf, int buf_size) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv_n"); + return this->recv_n (buf, buf_size, 0); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send_n (const void *buf, int len) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send_n"); + return this->send_n (buf, len, 0); +} + +ACE_INLINE int +ACE_SSL_SOCK_Stream::close_reader (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::close_reader"); + return this->stream_.close_reader (); +} + +ACE_INLINE int +ACE_SSL_SOCK_Stream::close_writer (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::close_writer"); + return this->stream_.close_writer (); +} + +ACE_INLINE int +ACE_SSL_SOCK_Stream::close (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::close"); + + if (this->ssl_ == 0 || this->get_handle () == ACE_INVALID_HANDLE) + return 0; // SSL_SOCK_Stream was never opened. + + // SSL_shutdown() returns 1 on successful shutdown of the SSL + // connection, not 0. + int const status = ::SSL_shutdown (this->ssl_); + + switch (::SSL_get_error (this->ssl_, status)) + { + case SSL_ERROR_NONE: + case SSL_ERROR_SYSCALL: // Ignore this error condition. + + // Reset the SSL object to allow another connection to be made + // using this ACE_SSL_SOCK_Stream instance. This prevents the + // previous SSL session state from being associated with the new + // SSL session/connection. + (void) ::SSL_clear (this->ssl_); + this->set_handle (ACE_INVALID_HANDLE); + return this->stream_.close (); + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + errno = EWOULDBLOCK; + break; + + default: + ACE_SSL_Context::report_error (); + + ACE_Errno_Guard error (errno); // Save/restore errno + (void) this->stream_.close (); + + return -1; + } + + return -1; +} + +ACE_INLINE ACE_SOCK_Stream & +ACE_SSL_SOCK_Stream::peer (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::peer"); + return this->stream_; +} + +ACE_INLINE SSL * +ACE_SSL_SOCK_Stream::ssl (void) const +{ + return this->ssl_; +} + +ACE_END_VERSIONED_NAMESPACE_DECL diff --git a/ACE/ace/SSL/ssl.mpc b/ACE/ace/SSL/ssl.mpc new file mode 100644 index 00000000000..1b8b5ed7c24 --- /dev/null +++ b/ACE/ace/SSL/ssl.mpc @@ -0,0 +1,13 @@ +// -*- MPC -*- +// $Id$ + +project(SSL) : acelib, ace_output, install, ace_openssl { + avoids += ace_for_tao + requires += ssl + sharedname = ACE_SSL + dynamicflags = ACE_SSL_BUILD_DLL + + pkgconfig_files { + ACE_SSL.pc.in + } +} diff --git a/ACE/ace/SSL/ssl_for_tao.mpc b/ACE/ace/SSL/ssl_for_tao.mpc new file mode 100644 index 00000000000..54915488d7e --- /dev/null +++ b/ACE/ace/SSL/ssl_for_tao.mpc @@ -0,0 +1,42 @@ +// -*- MPC -*- +// +// $Id$ + +project(SSL_FOR_TAO) : acelib, ace_output, install, ace_openssl { + requires += ssl ace_for_tao + sharedname = ACE_SSL_FOR_TAO + dynamicflags = ACE_SSL_BUILD_DLL + + Source_Files { + SSL_Context.cpp + SSL_SOCK.cpp + SSL_SOCK_Acceptor.cpp + SSL_SOCK_Connector.cpp + SSL_SOCK_Stream.cpp + } + + Header_Files { + SSL_Context.h + SSL_Export.h + SSL_SOCK.h + SSL_SOCK_Acceptor.h + SSL_SOCK_Connector.h + SSL_SOCK_Stream.h + sslconf.h + } + + Inline_Files { + SSL_SOCK.inl + SSL_SOCK_Acceptor.inl + SSL_SOCK_Connector.inl + SSL_Context.inl + SSL_SOCK_Stream.inl + } + + Template_Files { + } + + pkgconfig_files { + ACE_SSL.pc.in + } +} diff --git a/ACE/ace/SSL/sslconf.h b/ACE/ace/SSL/sslconf.h new file mode 100644 index 00000000000..f456558fce9 --- /dev/null +++ b/ACE/ace/SSL/sslconf.h @@ -0,0 +1,55 @@ +// -*- C++ -*- + +//============================================================================= +/** + * @file sslconf.h + * + * $Id$ + * + * @author Carlos O'Ryan <coryan@ece.uci.edu> + */ +//============================================================================= + + +#ifndef ACE_SSLCONF_H +#define ACE_SSLCONF_H + +#include /**/ "ace/pre.h" + +#include /**/ "ace/config-all.h" + +#if !defined (ACE_DEFAULT_SSL_CERT_FILE) +# ifdef WIN32 +# define ACE_DEFAULT_SSL_CERT_FILE "cert.pem" +# else +# define ACE_DEFAULT_SSL_CERT_FILE "/etc/ssl/cert.pem" +# endif /* WIN32 */ +#endif /* ACE_DEFAULT_SSL_CERT_FILE */ + +#if !defined (ACE_DEFAULT_SSL_CERT_DIR) +# ifdef WIN32 +# define ACE_DEFAULT_SSL_CERT_DIR "certs" +# else +# define ACE_DEFAULT_SSL_CERT_DIR "/etc/ssl/certs" +# endif /* WIN32 */ +#endif /* ACE_DEFAULT_SSL_CERT_DIR */ + +#if !defined (ACE_SSL_CERT_FILE_ENV) +#define ACE_SSL_CERT_FILE_ENV "SSL_CERT_FILE" +#endif /* ACE_SSL_CERT_FILE_ENV */ + +#if !defined (ACE_SSL_CERT_DIR_ENV) +#define ACE_SSL_CERT_DIR_ENV "SSL_CERT_DIR" +#endif /* ACE_SSL_CERT_DIR_ENV */ + +#if !defined (ACE_SSL_EGD_FILE_ENV) +#define ACE_SSL_EGD_FILE_ENV "SSL_EGD_FILE" +#endif /* ACE_SSL_EGD_FILE_ENV */ + +#if !defined (ACE_SSL_RAND_FILE_ENV) +#define ACE_SSL_RAND_FILE_ENV "SSL_RAND_FILE" +#endif /* ACE_SSL_RAND_FILE_ENV */ + +#include /**/ "ace/post.h" + +#endif /* ACE_SSLCONF_H */ |