gloox  1.0
tlsgnutlsbase.cpp
00001 /*
00002   Copyright (c) 2005-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 "tlsgnutlsbase.h"
00016 
00017 #ifdef HAVE_GNUTLS
00018 
00019 #include <errno.h>
00020 #include <stdlib.h>
00021 #include <string.h>
00022 
00023 #include <cstdlib>
00024 #include <cstring>
00025 
00026 namespace gloox
00027 {
00028 
00029   GnuTLSBase::GnuTLSBase( TLSHandler* th, const std::string& server )
00030     : TLSBase( th, server ), m_session( new gnutls_session_t ), m_buf( 0 ), m_bufsize( 17000 )
00031   {
00032     m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) );
00033   }
00034 
00035   GnuTLSBase::~GnuTLSBase()
00036   {
00037     free( m_buf );
00038     m_buf = 0;
00039     cleanup();
00040     delete m_session;
00041 // FIXME: It segfaults if more then one account uses
00042 // encryption at same time, so we comment it for now.
00043 // Do we need to deinit at all?
00044 //     gnutls_global_deinit();
00045   }
00046 
00047   bool GnuTLSBase::encrypt( const std::string& data )
00048   {
00049     if( !m_secure )
00050     {
00051       handshake();
00052       return true;
00053     }
00054 
00055     ssize_t ret = 0;
00056     std::string::size_type sum = 0;
00057     do
00058     {
00059       ret = gnutls_record_send( *m_session, data.c_str() + sum, data.length() - sum );
00060       sum += ret;
00061     }
00062     while( ( ret == GNUTLS_E_AGAIN ) || ( ret == GNUTLS_E_INTERRUPTED ) || sum < data.length() );
00063     return true;
00064   }
00065 
00066   int GnuTLSBase::decrypt( const std::string& data )
00067   {
00068     m_recvBuffer += data;
00069 
00070     if( !m_secure )
00071     {
00072       handshake();
00073       return static_cast<int>( data.length() );
00074     }
00075 
00076     int sum = 0;
00077     int ret = 0;
00078     do
00079     {
00080       ret = static_cast<int>( gnutls_record_recv( *m_session, m_buf, m_bufsize ) );
00081 
00082       if( ret > 0 && m_handler )
00083       {
00084         m_handler->handleDecryptedData( this, std::string( m_buf, ret ) );
00085         sum += ret;
00086       }
00087     }
00088     while( ret > 0 );
00089 
00090     return sum;
00091   }
00092 
00093   void GnuTLSBase::cleanup()
00094   {
00095     if( !m_mutex.trylock() )
00096       return;
00097 
00098     TLSHandler* handler = m_handler;
00099     m_handler = 0;
00100     gnutls_bye( *m_session, GNUTLS_SHUT_RDWR );
00101     gnutls_db_remove_session( *m_session );
00102     gnutls_credentials_clear( *m_session );
00103     if( m_secure )
00104       gnutls_deinit( *m_session );
00105 
00106     m_secure = false;
00107     m_valid = false;
00108     delete m_session;
00109     m_session = 0;
00110     m_session = new gnutls_session_t;
00111     m_handler = handler;
00112 
00113     m_mutex.unlock();
00114   }
00115 
00116   bool GnuTLSBase::handshake()
00117   {
00118     if( !m_handler )
00119       return false;
00120 
00121     int ret = gnutls_handshake( *m_session );
00122     if( ret < 0 && gnutls_error_is_fatal( ret ) )
00123     {
00124       gnutls_perror( ret );
00125       gnutls_db_remove_session( *m_session );
00126       gnutls_deinit( *m_session );
00127       m_valid = false;
00128 
00129       m_handler->handleHandshakeResult( this, false, m_certInfo );
00130       return false;
00131     }
00132     else if( ret == GNUTLS_E_AGAIN )
00133     {
00134       return true;
00135     }
00136 
00137     m_secure = true;
00138 
00139     getCertInfo();
00140 
00141     m_handler->handleHandshakeResult( this, true, m_certInfo );
00142     return true;
00143   }
00144 
00145   ssize_t GnuTLSBase::pullFunc( void* data, size_t len )
00146   {
00147     ssize_t cpy = ( len > m_recvBuffer.length() ) ? ( m_recvBuffer.length() ) : ( len );
00148     if( cpy > 0 )
00149     {
00150       memcpy( data, (const void*)m_recvBuffer.c_str(), cpy );
00151       m_recvBuffer.erase( 0, cpy );
00152       return cpy;
00153     }
00154     else
00155     {
00156       errno = EAGAIN;
00157       return GNUTLS_E_AGAIN;
00158     }
00159   }
00160 
00161   ssize_t GnuTLSBase::pullFunc( gnutls_transport_ptr_t ptr, void* data, size_t len )
00162   {
00163     return static_cast<GnuTLSBase*>( ptr )->pullFunc( data, len );
00164   }
00165 
00166   ssize_t GnuTLSBase::pushFunc( const void* data, size_t len )
00167   {
00168     if( m_handler )
00169       m_handler->handleEncryptedData( this, std::string( (const char*)data, len ) );
00170 
00171     return len;
00172   }
00173 
00174   ssize_t GnuTLSBase::pushFunc( gnutls_transport_ptr_t ptr, const void* data, size_t len )
00175   {
00176     return static_cast<GnuTLSBase*>( ptr )->pushFunc( data, len );
00177   }
00178 
00179 }
00180 
00181 #endif // HAVE_GNUTLS