tlsopenssl.cpp

00001 /*
00002   Copyright (c) 2005-2008 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 #include <ctime>
00022 #include <cstdlib>
00023 
00024 namespace gloox
00025 {
00026 
00027   OpenSSL::OpenSSL( TLSHandler *th, const std::string& server )
00028     : TLSBase( th, server ), m_ssl( 0 ), m_ctx( 0 ), m_buf( 0 ), m_bufsize( 17000 )
00029   {
00030     m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) );
00031 
00032     SSL_library_init();
00033 
00034     SSL_COMP_add_compression_method( 1, COMP_zlib() );
00035 
00036     m_ctx = SSL_CTX_new( TLSv1_client_method() );
00037     if( !m_ctx )
00038       return;
00039 
00040     if( !SSL_CTX_set_cipher_list( m_ctx, "HIGH:MEDIUM:AES:@STRENGTH" ) )
00041       return;
00042 
00043     m_ssl = SSL_new( m_ctx );
00044     SSL_set_connect_state( m_ssl );
00045 
00046     if( !BIO_new_bio_pair( &m_ibio, 0, &m_nbio, 0 ) )
00047     {
00048       return;
00049     }
00050     SSL_set_bio( m_ssl, m_ibio, m_ibio );
00051     SSL_set_mode( m_ssl, SSL_MODE_AUTO_RETRY );
00052   }
00053 
00054   OpenSSL::~OpenSSL()
00055   {
00056     m_handler = 0;
00057     free( m_buf );
00058     SSL_CTX_free( m_ctx );
00059     SSL_shutdown( m_ssl );
00060     SSL_free( m_ssl );
00061     BIO_free( m_nbio );
00062     cleanup();
00063   }
00064 
00065   bool OpenSSL::encrypt( const std::string& data )
00066   {
00067     m_sendBuffer += data;
00068 
00069     if( !m_secure )
00070     {
00071       handshake();
00072       return 0;
00073     }
00074 
00075     doTLSOperation( TLSWrite );
00076     return true;
00077   }
00078 
00079   int OpenSSL::decrypt( const std::string& data )
00080   {
00081     m_recvBuffer += data;
00082 
00083     if( !m_secure )
00084     {
00085       handshake();
00086       return 0;
00087     }
00088 
00089     doTLSOperation( TLSRead );
00090     return true;
00091   }
00092 
00093   void OpenSSL::setCACerts( const StringList& cacerts )
00094   {
00095     m_cacerts = cacerts;
00096 
00097     StringList::const_iterator it = m_cacerts.begin();
00098     for( ; it != m_cacerts.end(); ++it )
00099       SSL_CTX_load_verify_locations( m_ctx, (*it).c_str(), 0 );
00100   }
00101 
00102   void OpenSSL::setClientCert( const std::string& clientKey, const std::string& clientCerts )
00103   {
00104     m_clientKey = clientKey;
00105     m_clientCerts = clientCerts;
00106 
00107     if( !m_clientKey.empty() && !m_clientCerts.empty() )
00108     {
00109       SSL_CTX_use_certificate_chain_file( m_ctx, m_clientCerts.c_str() );
00110       SSL_CTX_use_PrivateKey_file( m_ctx, m_clientKey.c_str(), SSL_FILETYPE_PEM );
00111     }
00112   }
00113 
00114   void OpenSSL::cleanup()
00115   {
00116     m_secure = false;
00117     m_valid = false;
00118   }
00119 
00120   void OpenSSL::doTLSOperation( TLSOperation op )
00121   {
00122     if( !m_handler )
00123       return;
00124 
00125     int ret = 0;
00126     bool onceAgain = false;
00127 
00128     do
00129     {
00130       switch( op )
00131       {
00132         case TLSHandshake:
00133           ret = SSL_connect( m_ssl );
00134           break;
00135         case TLSWrite:
00136           ret = SSL_write( m_ssl, m_sendBuffer.c_str(), m_sendBuffer.length() );
00137           break;
00138         case TLSRead:
00139           ret = SSL_read( m_ssl, m_buf, m_bufsize );
00140           break;
00141       }
00142 
00143       switch( SSL_get_error( m_ssl, ret ) )
00144       {
00145         case SSL_ERROR_WANT_READ:
00146         case SSL_ERROR_WANT_WRITE:
00147           pushFunc();
00148           break;
00149         case SSL_ERROR_NONE:
00150           if( op == TLSHandshake )
00151             m_secure = true;
00152           else if( op == TLSWrite )
00153             m_sendBuffer.erase( 0, ret );
00154           else if( op == TLSRead )
00155             m_handler->handleDecryptedData( this, std::string( m_buf, ret ) );
00156           pushFunc();
00157           break;
00158         default:
00159           if( !m_secure )
00160             m_handler->handleHandshakeResult( this, false, m_certInfo );
00161           return;
00162           break;
00163       }
00164       if( !onceAgain && !m_recvBuffer.length() )
00165         onceAgain = true;
00166       else if( onceAgain )
00167         onceAgain = false;
00168     }
00169     while( ( onceAgain || m_recvBuffer.length() ) && ( !m_secure || op == TLSRead ) );
00170   }
00171 
00172   int OpenSSL::openSSLTime2UnixTime( const char* time_string )
00173   {
00174     char tstring[19];
00175 
00176     // making seperate c string out of time string
00177     int m = 0;
00178     for( int n = 0; n < 12; n += 2 )
00179     {
00180       tstring[m] = time_string[n];
00181       tstring[m + 1] = time_string[n + 1];
00182       tstring[m + 2] = 0;
00183       m += 3;
00184     }
00185 
00186     // converting to struct tm
00187     tm time_st;
00188     time_st.tm_year = ( atoi( &tstring[3 * 0] ) >= 70 ) ? atoi( &tstring[3 * 0] )
00189                                                         : atoi( &tstring[3 * 0] ) + 100;
00190     time_st.tm_mon = atoi( &tstring[3 * 1] ) - 1;
00191     time_st.tm_mday = atoi( &tstring[3 * 2] );
00192     time_st.tm_hour = atoi( &tstring[3 * 3] );
00193     time_st.tm_min = atoi( &tstring[3 * 4] );
00194     time_st.tm_sec = atoi( &tstring[3 * 5] );
00195 
00196     time_t unixt = mktime( &time_st );
00197     return unixt;
00198   }
00199 
00200   bool OpenSSL::handshake()
00201   {
00202 
00203     doTLSOperation( TLSHandshake );
00204 
00205     if( !m_secure )
00206       return true;
00207 
00208     int res = SSL_get_verify_result( m_ssl );
00209     if( res != X509_V_OK )
00210       m_certInfo.status = CertInvalid;
00211     else
00212       m_certInfo.status = CertOk;
00213 
00214     X509 *peer = SSL_get_peer_certificate( m_ssl );
00215     if( peer )
00216     {
00217       char peer_CN[256];
00218       X509_NAME_get_text_by_NID( X509_get_issuer_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
00219       m_certInfo.issuer = peer_CN;
00220       X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
00221       m_certInfo.server = peer_CN;
00222       m_certInfo.date_from = openSSLTime2UnixTime( (char*)(peer->cert_info->validity->notBefore->data) );
00223       m_certInfo.date_to = openSSLTime2UnixTime( (char*)(peer->cert_info->validity->notAfter->data) );
00224       std::string p;
00225       p.assign( peer_CN );
00226       std::transform( p.begin(), p.end(), p.begin(), std::tolower );
00227       if( p != m_server )
00228         m_certInfo.status |= CertWrongPeer;
00229 
00230       if( ASN1_UTCTIME_cmp_time_t( X509_get_notBefore( peer ), time( 0 ) ) != -1 )
00231         m_certInfo.status |= CertNotActive;
00232 
00233       if( ASN1_UTCTIME_cmp_time_t( X509_get_notAfter( peer ), time( 0 ) ) != 1 )
00234         m_certInfo.status |= CertExpired;
00235     }
00236     else
00237     {
00238       m_certInfo.status = CertInvalid;
00239     }
00240 
00241     const char *tmp;
00242     tmp = SSL_get_cipher_name( m_ssl );
00243     if( tmp )
00244       m_certInfo.cipher = tmp;
00245 
00246     tmp = SSL_get_cipher_version( m_ssl );
00247     if( tmp )
00248       m_certInfo.protocol = tmp;
00249 
00250     m_valid = true;
00251 
00252     m_handler->handleHandshakeResult( this, true, m_certInfo );
00253     return true;
00254   }
00255 
00256   void OpenSSL::pushFunc()
00257   {
00258     int wantwrite;
00259     size_t wantread;
00260     int frombio;
00261     int tobio;
00262 
00263     while( ( wantwrite = BIO_ctrl_pending( m_nbio ) ) > 0 )
00264     {
00265       if( wantwrite > m_bufsize )
00266         wantwrite = m_bufsize;
00267 
00268       if( !wantwrite )
00269         break;
00270 
00271       frombio = BIO_read( m_nbio, m_buf, wantwrite );
00272 
00273       if( m_handler )
00274         m_handler->handleEncryptedData( this, std::string( m_buf, frombio ) );
00275     }
00276 
00277     while( ( wantread = BIO_ctrl_get_read_request( m_nbio ) ) > 0 )
00278     {
00279       if( wantread > m_recvBuffer.length() )
00280         wantread = m_recvBuffer.length();
00281 
00282       if( !wantread )
00283         break;
00284 
00285       tobio = BIO_write( m_nbio, m_recvBuffer.c_str(), wantread );
00286       m_recvBuffer.erase( 0, tobio );
00287     }
00288   }
00289 
00290 }
00291 
00292 #endif // HAVE_OPENSSL

Generated on Sun Apr 27 11:08:14 2008 for gloox by  doxygen 1.5.5