gloox 1.0

tlsopensslbase.cpp

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