Author: tabish Date: Tue May 25 20:17:26 2010 New Revision: 948196 URL: http://svn.apache.org/viewvc?rev=948196&view=rev Log: https://issues.apache.org/activemq/browse/AMQCPP-140
Add code to allow the OpenSSLSocket to be used as either a client or a server based socket. Modified: activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.cpp activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.h activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/net/ssl/SSLSocket.h Modified: activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.cpp URL: http://svn.apache.org/viewvc/activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.cpp?rev=948196&r1=948195&r2=948196&view=diff ============================================================================== --- activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.cpp (original) +++ activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.cpp Tue May 25 20:17:26 2010 @@ -35,6 +35,7 @@ #include <decaf/internal/net/ssl/openssl/OpenSSLSocketException.h> #include <decaf/internal/net/ssl/openssl/OpenSSLSocketInputStream.h> #include <decaf/internal/net/ssl/openssl/OpenSSLSocketOutputStream.h> +#include <decaf/util/concurrent/Mutex.h> using namespace decaf; using namespace decaf::lang; @@ -42,6 +43,7 @@ using namespace decaf::lang::exceptions; using namespace decaf::io; using namespace decaf::net; using namespace decaf::net::ssl; +using namespace decaf::util::concurrent; using namespace decaf::internal; using namespace decaf::internal::net; using namespace decaf::internal::net::ssl; @@ -64,10 +66,17 @@ namespace openssl { #endif bool needsClientAuth; bool wantsClientAuth; + bool useClientMode; + bool handshakeStarted; + bool handshakeCompleted; + std::string commonName; + + Mutex handshakeLock; public: - SocketData() : ssl( NULL ), needsClientAuth( false ), wantsClientAuth( false ) { + SocketData() : ssl( NULL ), needsClientAuth( false ), wantsClientAuth( false ), + useClientMode( true ), handshakeStarted( false ), handshakeCompleted( false ) { } ~SocketData() { @@ -139,7 +148,12 @@ void OpenSSLSocket::connect( const std:: try{ #ifdef HAVE_OPENSSL + + // Perform the actual Socket connection work SSLSocket::connect( host, port, timeout ); + + // If we actually connected then we can connect the Socket to an OpenSSL + // BIO filter so that we can use it in OpenSSL APIs. if( isConnected() ) { BIO* bio = BIO_new( BIO_s_socket() ); @@ -159,26 +173,9 @@ void OpenSSLSocket::connect( const std:: BIO_set_fd( bio, (int)fd->getValue(), BIO_NOCLOSE ); SSL_set_bio( this->data->ssl, bio, bio ); - // Since we are a client we want to enforce peer verification, we set a - // callback so we can collect data on why a verify failed for debugging. - SSL_set_verify( this->data->ssl, SSL_VERIFY_PEER, SocketData::verifyCallback ); - - int result = SSL_connect( this->data->ssl ); - - // Checks the error status, when things go right we still perform a deeper - // check on the provided certificate to ensure that it matches the host name - // that we connected to, this prevents someone from using any certificate - // signed by a signing authority that we trust. - switch( SSL_get_error( this->data->ssl, result ) ) { - case SSL_ERROR_NONE: - verifyServerCert( host ); - return; - case SSL_ERROR_SSL: - case SSL_ERROR_ZERO_RETURN: - case SSL_ERROR_SYSCALL: - SSLSocket::close(); - throw OpenSSLSocketException( __FILE__, __LINE__ ); - } + // Later when startHandshake is called we will check for this common name + // in the provided certificate + this->data->commonName = host; } #else throw SocketException( __FILE__, __LINE__, "Not Supported" ); @@ -310,6 +307,123 @@ void OpenSSLSocket::setEnabledProtocols( } //////////////////////////////////////////////////////////////////////////////// +void OpenSSLSocket::startHandshake() { + + if( !this->isConnected() ) { + throw IOException( __FILE__, __LINE__, "Socket is not connected." ); + } + + if( this->isClosed() ) { + throw IOException( __FILE__, __LINE__, "Socket already closed." ); + } + + try { + + synchronized( &(this->data->handshakeLock ) ) { + + if( this->data->handshakeStarted ) { + return; + } + + this->data->handshakeStarted = true; + + if( this->data->useClientMode ) { + + // Since we are a client we want to enforce peer verification, we set a + // callback so we can collect data on why a verify failed for debugging. + SSL_set_verify( this->data->ssl, SSL_VERIFY_PEER, SocketData::verifyCallback ); + + int result = SSL_connect( this->data->ssl ); + + // Checks the error status, when things go right we still perform a deeper + // check on the provided certificate to ensure that it matches the host name + // that we connected to, this prevents someone from using any certificate + // signed by a signing authority that we trust. + switch( SSL_get_error( this->data->ssl, result ) ) { + case SSL_ERROR_NONE: + verifyServerCert( this->data->commonName ); + std::cout << "OpenSSLSocket::startHandshake() - Verified name: " + << this->data->commonName << std::endl; + break; + case SSL_ERROR_SSL: + case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_SYSCALL: + SSLSocket::close(); + throw OpenSSLSocketException( __FILE__, __LINE__ ); + } + + } else { // We are in Server Mode. + + int mode = SSL_VERIFY_NONE; + + if( this->data->wantsClientAuth ) { + mode = SSL_VERIFY_PEER; + } + + if( this->data->needsClientAuth ) { + mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } + + // Since we are a client we want to enforce peer verification, we set a + // callback so we can collect data on why a verify failed for debugging. + SSL_set_verify( this->data->ssl, mode, SocketData::verifyCallback ); + + int result = SSL_accept( this->data->ssl ); + + if( result != SSL_ERROR_NONE ) { + SSLSocket::close(); + throw OpenSSLSocketException( __FILE__, __LINE__ ); + } + } + + this->data->handshakeCompleted = true; + } + } + DECAF_CATCH_RETHROW( IOException ) + DECAF_CATCHALL_THROW( IOException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void OpenSSLSocket::setUseClientMode( bool value ) { + + synchronized( &( this->data->handshakeLock ) ) { + if( this->data->handshakeStarted ) { + throw IllegalArgumentException( + __FILE__, __LINE__, "Handshake has already been started cannot change mode." ); + } + + this->data->useClientMode = value; + } +} + +//////////////////////////////////////////////////////////////////////////////// +bool OpenSSLSocket::getUseClientMode() const { + return this->data->useClientMode; +} + +//////////////////////////////////////////////////////////////////////////////// +void OpenSSLSocket::setNeedClientAuth( bool value ) { + this->data->needsClientAuth = value; + this->data->wantsClientAuth = false; +} + +//////////////////////////////////////////////////////////////////////////////// +bool OpenSSLSocket::getNeedClientAuth() const { + return this->data->needsClientAuth; +} + +//////////////////////////////////////////////////////////////////////////////// +void OpenSSLSocket::setWantClientAuth( bool value ) { + this->data->wantsClientAuth = value; + this->data->needsClientAuth = false; +} + +//////////////////////////////////////////////////////////////////////////////// +bool OpenSSLSocket::getWantClientAuth() const { + return this->data->wantsClientAuth; +} + +//////////////////////////////////////////////////////////////////////////////// int OpenSSLSocket::read( unsigned char* buffer, int size, int offset, int length ) { try{ @@ -347,6 +461,11 @@ int OpenSSLSocket::read( unsigned char* } #ifdef HAVE_OPENSSL + + if( !this->data->handshakeCompleted ) { + this->startHandshake(); + } + // Read data from the socket. int result = SSL_read( this->data->ssl, buffer + offset, length ); @@ -408,6 +527,11 @@ void OpenSSLSocket::write( const unsigne } #ifdef HAVE_OPENSSL + + if( !this->data->handshakeCompleted ) { + this->startHandshake(); + } + int remaining = length; while( remaining > 0 && !isClosed() ) { Modified: activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.h URL: http://svn.apache.org/viewvc/activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.h?rev=948196&r1=948195&r2=948196&view=diff ============================================================================== --- activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.h (original) +++ activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/internal/net/ssl/openssl/OpenSSLSocket.h Tue May 25 20:17:26 2010 @@ -133,6 +133,41 @@ namespace openssl { */ virtual void setEnabledProtocols( const std::vector<std::string>& protocols ); + /** + * {...@inheritdoc} + */ + virtual void startHandshake(); + + /** + * {...@inheritdoc} + */ + virtual void setUseClientMode( bool value ); + + /** + * {...@inheritdoc} + */ + virtual bool getUseClientMode() const; + + /** + * {...@inheritdoc} + */ + virtual void setNeedClientAuth( bool value ); + + /** + * {...@inheritdoc} + */ + virtual bool getNeedClientAuth() const; + + /** + * {...@inheritdoc} + */ + virtual void setWantClientAuth( bool value ); + + /** + * {...@inheritdoc} + */ + virtual bool getWantClientAuth() const; + public: /** Modified: activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/net/ssl/SSLSocket.h URL: http://svn.apache.org/viewvc/activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/net/ssl/SSLSocket.h?rev=948196&r1=948195&r2=948196&view=diff ============================================================================== --- activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/net/ssl/SSLSocket.h (original) +++ activemq/activemq-cpp/trunk/activemq-cpp/src/main/decaf/net/ssl/SSLSocket.h Tue May 25 20:17:26 2010 @@ -124,6 +124,86 @@ namespace ssl { */ virtual void setSSLParameters( const SSLParameters& value ); + /** + * Initiates a handshake for this SSL Connection, this can be necessary for several reasons such + * as using new encryption keys, or starting a new session. + * + * When called for the first time after the socket connects this method blocks until the handshake + * is completed. The provider is not require to support multiple handshakes and can throw an + * IOException to indicate an error. + * + * @throw IOException if an I/O error occurs while performing the Handshake + */ + virtual void startHandshake() = 0; + + /** + * Determines the mode that the socket uses when a handshake is initiated, client or server. + * + * This method must be called prior to any handshake attempts on this Socket, once a handshake + * has be initiated this socket remains the the set mode; client or server, for the life of + * this object. + * + * @param value + * The mode setting, true for client or false for server. + * + * @throw IllegalArguementException if the handshake process has begun and mode is lcoked. + */ + virtual void setUseClientMode( bool value ) = 0; + + /** + * Gets whether this Socket is in Client or Server mode, true indicates that the mode is + * set to Client. + * + * @return true if the Socket is in Client mode, false otherwise. + */ + virtual bool getUseClientMode() const = 0; + + /** + * Sets the Socket to require that a client authenticate itself by sending a valid Certificate that + * is trusted by this Server mode socket. This option only applies to sockets in the Server mode. + * + * If the option is enabled an the client does not provide a certificate then the handshake is + * considered failed and the connection is refused. Calling this method resets any previous + * value for this option as well as clears any value set in the setWantClientAuth method. + * + * @param value + * The value indicating if a client is required to authenticate itself or not. + */ + virtual void setNeedClientAuth( bool value ) = 0; + + /** + * Returns if this socket is configured to require client authentication, true means that is has + * and that clients that failed to authenticate will be rejected. This option is only useful when + * the socket is operating in server mode. + * + * @return true if client authentication is required. + */ + virtual bool getNeedClientAuth() const = 0; + + /** + * Sets the Socket to request that a client authenticate itself by sending a valid Certificate that + * is trusted by this Server mode socket. This option only applies to sockets in the Server mode. + * + * If the option is enabled an the client does not provide a certificate then the handshake is + * considered to have succeeded, if it does send a certificate and that certificate is invalid the + * the handshake will fail. Calling this method resets any previous value for this option as well + * as clears any value set in the setNeedClientAuth method. + * + * @param value + * The value indicating if a client is requested to authenticate itself or not. + */ + virtual void setWantClientAuth( bool value ) = 0; + + /** + * Returns if this socket is configured to request client authentication, true means that is has + * and that clients that failed to authenticate will be rejected but that cleints that do not send + * a certificate are not considered to have failed authentication. This option is only useful when + * the socket is operating in server mode. + * + * @return true if client authentication is required. + */ + virtual bool getWantClientAuth() const = 0; + }; }}}