Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | Related Pages

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

Generated on Sun Dec 28 22:10:18 2008 for gloox by  doxygen 1.4.1