tlsopenssl.cpp

00001 /*
00002   Copyright (c) 2005-2007 by Jakob Schroeter <js@camaya.net>
00003   This file is part of the gloox library. http://camaya.net/gloox
00004 
00005   This software is distributed under a license. The full license
00006   agreement can be found in the file LICENSE in this distribution.
00007   This software may not be copied, modified, sold or distributed
00008   other than expressed in the named license agreement.
00009 
00010   This software is distributed without any warranty.
00011 */
00012 
00013 
00014 
00015 #include "tlsopenssl.h"
00016 
00017 #ifdef HAVE_OPENSSL
00018 
00019 #include <algorithm>
00020 #include <cctype>
00021 
00022 namespace gloox
00023 {
00024 
00025   OpenSSL::OpenSSL( TLSHandler *th, const std::string& server )
00026     : TLSBase( th, server ), m_ssl( 0 ), m_ctx( 0 ), m_buf( 0 ), m_bufsize( 17000 )
00027   {
00028     m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) );
00029 
00030     SSL_library_init();
00031 
00032     SSL_COMP_add_compression_method( 1, COMP_zlib() );
00033 
00034     m_ctx = SSL_CTX_new( TLSv1_client_method() );
00035     if( !m_ctx )
00036       return;
00037 
00038     if( !SSL_CTX_set_cipher_list( m_ctx, "HIGH:MEDIUM:AES:@STRENGTH" ) )
00039       return;
00040 
00041     m_ssl = SSL_new( m_ctx );
00042     SSL_set_connect_state( m_ssl );
00043 
00044     if( !BIO_new_bio_pair( &m_ibio, 0, &m_nbio, 0 ) )
00045     {
00046       return;
00047     }
00048     SSL_set_bio( m_ssl, m_ibio, m_ibio );
00049     SSL_set_mode( m_ssl, SSL_MODE_AUTO_RETRY );
00050   }
00051 
00052   OpenSSL::~OpenSSL()
00053   {
00054     m_handler = 0;
00055     free( m_buf );
00056     SSL_CTX_free( m_ctx );
00057     SSL_shutdown( m_ssl );
00058     SSL_free( m_ssl );
00059     BIO_free( m_nbio );
00060     cleanup();
00061   }
00062 
00063   bool OpenSSL::encrypt( const std::string& data )
00064   {
00065     m_sendBuffer += data;
00066 
00067     if( !m_secure )
00068     {
00069       handshake();
00070       return 0;
00071     }
00072 
00073     doTLSOperation( TLSWrite );
00074     return true;
00075   }
00076 
00077   int OpenSSL::decrypt( const std::string& data )
00078   {
00079     m_recvBuffer += data;
00080 
00081     if( !m_secure )
00082     {
00083       handshake();
00084       return 0;
00085     }
00086 
00087     doTLSOperation( TLSRead );
00088     return true;
00089   }
00090 
00091   void OpenSSL::setCACerts( const StringList& cacerts )
00092   {
00093     m_cacerts = cacerts;
00094 
00095     StringList::const_iterator it = m_cacerts.begin();
00096     for( ; it != m_cacerts.end(); ++it )
00097       SSL_CTX_load_verify_locations( m_ctx, (*it).c_str(), 0 );
00098   }
00099 
00100   void OpenSSL::setClientCert( const std::string& clientKey, const std::string& clientCerts )
00101   {
00102     m_clientKey = clientKey;
00103     m_clientCerts = clientCerts;
00104 
00105     if( !m_clientKey.empty() && !m_clientCerts.empty() )
00106     {
00107       SSL_CTX_use_certificate_chain_file( m_ctx, m_clientCerts.c_str() );
00108       SSL_CTX_use_PrivateKey_file( m_ctx, m_clientKey.c_str(), SSL_FILETYPE_PEM );
00109     }
00110   }
00111 
00112   void OpenSSL::cleanup()
00113   {
00114     m_secure = false;
00115     m_valid = false;
00116   }
00117 
00118   void OpenSSL::doTLSOperation( TLSOperation op )
00119   {
00120     if( !m_handler )
00121       return;
00122 
00123     int ret = 0;
00124     bool onceAgain = false;
00125 
00126     do
00127     {
00128       switch( op )
00129       {
00130         case TLSHandshake:
00131           ret = SSL_connect( m_ssl );
00132           break;
00133         case TLSWrite:
00134           ret = SSL_write( m_ssl, m_sendBuffer.c_str(), m_sendBuffer.length() );
00135           break;
00136         case TLSRead:
00137           ret = SSL_read( m_ssl, m_buf, m_bufsize );
00138           break;
00139       }
00140 
00141       switch( SSL_get_error( m_ssl, ret ) )
00142       {
00143         case SSL_ERROR_WANT_READ:
00144         case SSL_ERROR_WANT_WRITE:
00145           pushFunc();
00146           break;
00147         case SSL_ERROR_NONE:
00148           if( op == TLSHandshake )
00149             m_secure = true;
00150           else if( op == TLSWrite )
00151             m_sendBuffer.erase( 0, ret );
00152           else if( op == TLSRead )
00153             m_handler->handleDecryptedData( this, std::string( m_buf, ret ) );
00154           pushFunc();
00155           break;
00156         default:
00157           if( !m_secure )
00158             m_handler->handleHandshakeResult( this, false, m_certInfo );
00159           return;
00160           break;
00161       }
00162       if( !onceAgain && !m_recvBuffer.length() )
00163         onceAgain = true;
00164       else if( onceAgain )
00165         onceAgain = false;
00166     }
00167     while( ( onceAgain || m_recvBuffer.length() ) && ( !m_secure || op == TLSRead ) );
00168   }
00169 
00170   bool OpenSSL::handshake()
00171   {
00172 
00173     doTLSOperation( TLSHandshake );
00174 
00175     if( !m_secure )
00176       return true;
00177 
00178     int res = SSL_get_verify_result( m_ssl );
00179     if( res != X509_V_OK )
00180       m_certInfo.status = CertInvalid;
00181     else
00182       m_certInfo.status = CertOk;
00183 
00184     X509 *peer = SSL_get_peer_certificate( m_ssl );
00185     if( peer )
00186     {
00187       char peer_CN[256];
00188       X509_NAME_get_text_by_NID( X509_get_issuer_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
00189       m_certInfo.issuer = peer_CN;
00190       X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
00191       m_certInfo.server = peer_CN;
00192       std::string p;
00193       p.assign( peer_CN );
00194       std::transform( p.begin(), p.end(), p.begin(), std::tolower );
00195       if( p != m_server )
00196         m_certInfo.status |= CertWrongPeer;
00197 
00198       if( ASN1_UTCTIME_cmp_time_t( X509_get_notBefore( peer ), time( 0 ) ) != -1 )
00199         m_certInfo.status |= CertNotActive;
00200 
00201       if( ASN1_UTCTIME_cmp_time_t( X509_get_notAfter( peer ), time( 0 ) ) != 1 )
00202         m_certInfo.status |= CertExpired;
00203     }
00204     else
00205     {
00206       m_certInfo.status = CertInvalid;
00207     }
00208 
00209     const char *tmp;
00210     tmp = SSL_get_cipher_name( m_ssl );
00211     if( tmp )
00212       m_certInfo.cipher = tmp;
00213 
00214     tmp = SSL_get_cipher_version( m_ssl );
00215     if( tmp )
00216       m_certInfo.protocol = tmp;
00217 
00218     m_valid = true;
00219 
00220     m_handler->handleHandshakeResult( this, true, m_certInfo );
00221     return true;
00222   }
00223 
00224   void OpenSSL::pushFunc()
00225   {
00226     int wantwrite;
00227     size_t wantread;
00228     int frombio;
00229     int tobio;
00230 
00231     while( ( wantwrite = BIO_ctrl_pending( m_nbio ) ) > 0 )
00232     {
00233       if( wantwrite > m_bufsize )
00234         wantwrite = m_bufsize;
00235 
00236       if( !wantwrite )
00237         break;
00238 
00239       frombio = BIO_read( m_nbio, m_buf, wantwrite );
00240 
00241       if( m_handler )
00242         m_handler->handleEncryptedData( this, std::string( m_buf, frombio ) );
00243     }
00244 
00245     while( ( wantread = BIO_ctrl_get_read_request( m_nbio ) ) > 0 )
00246     {
00247       if( wantread > m_recvBuffer.length() )
00248         wantread = m_recvBuffer.length();
00249 
00250       if( !wantread )
00251         break;
00252 
00253       tobio = BIO_write( m_nbio, m_recvBuffer.c_str(), wantread );
00254       m_recvBuffer.erase( 0, tobio );
00255     }
00256   }
00257 
00258 }
00259 
00260 #endif // HAVE_OPENSSL

Generated on Sat Nov 10 08:50:27 2007 for gloox by  doxygen 1.5.3-20071008