gloox 1.0
tlsgnutlsclient.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 "tlsgnutlsclient.h"
00016 
00017 #ifdef HAVE_GNUTLS
00018 
00019 #include <errno.h>
00020 
00021 namespace gloox
00022 {
00023 
00024   GnuTLSClient::GnuTLSClient( TLSHandler* th, const std::string& server )
00025     : GnuTLSBase( th, server )
00026   {
00027   }
00028 
00029   GnuTLSClient::~GnuTLSClient()
00030   {
00031   }
00032 
00033   void GnuTLSClient::cleanup()
00034   {
00035     GnuTLSBase::cleanup();
00036     init();
00037   }
00038 
00039   bool GnuTLSClient::init( const std::string& clientKey,
00040                            const std::string& clientCerts,
00041                            const StringList& cacerts )
00042   {
00043     const int protocolPriority[] = {
00044 #ifdef GNUTLS_TLS1_2
00045       GNUTLS_TLS1_2,
00046 #endif
00047       GNUTLS_TLS1_1, GNUTLS_TLS1, 0 };
00048     const int kxPriority[]       = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_DHE_DSS, 0 };
00049     const int cipherPriority[]   = { GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_CBC,
00050                                      GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_ARCFOUR, 0 };
00051     const int compPriority[]     = { GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 };
00052     const int macPriority[]      = { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 };
00053 
00054     if( m_initLib && gnutls_global_init() != 0 )
00055       return false;
00056 
00057     if( gnutls_certificate_allocate_credentials( &m_credentials ) < 0 )
00058       return false;
00059 
00060     if( gnutls_init( m_session, GNUTLS_CLIENT ) != 0 )
00061     {
00062       gnutls_certificate_free_credentials( m_credentials );
00063       return false;
00064     }
00065 
00066     gnutls_protocol_set_priority( *m_session, protocolPriority );
00067     gnutls_cipher_set_priority( *m_session, cipherPriority );
00068     gnutls_compression_set_priority( *m_session, compPriority );
00069     gnutls_kx_set_priority( *m_session, kxPriority );
00070     gnutls_mac_set_priority( *m_session, macPriority );
00071     gnutls_credentials_set( *m_session, GNUTLS_CRD_CERTIFICATE, m_credentials );
00072 
00073     gnutls_transport_set_ptr( *m_session, (gnutls_transport_ptr_t)this );
00074     gnutls_transport_set_push_function( *m_session, pushFunc );
00075     gnutls_transport_set_pull_function( *m_session, pullFunc );
00076 
00077     m_valid = true;
00078     return true;
00079   }
00080 
00081   void GnuTLSClient::setCACerts( const StringList& cacerts )
00082   {
00083     m_cacerts = cacerts;
00084 
00085     StringList::const_iterator it = m_cacerts.begin();
00086     for( ; it != m_cacerts.end(); ++it )
00087       gnutls_certificate_set_x509_trust_file( m_credentials, (*it).c_str(), GNUTLS_X509_FMT_PEM );
00088   }
00089 
00090   void GnuTLSClient::setClientCert( const std::string& clientKey, const std::string& clientCerts )
00091   {
00092     m_clientKey = clientKey;
00093     m_clientCerts = clientCerts;
00094 
00095     if( !m_clientKey.empty() && !m_clientCerts.empty() )
00096     {
00097       gnutls_certificate_set_x509_key_file( m_credentials, m_clientCerts.c_str(),
00098                                             m_clientKey.c_str(), GNUTLS_X509_FMT_PEM );
00099     }
00100   }
00101 
00102   void GnuTLSClient::getCertInfo()
00103   {
00104     unsigned int status;
00105     bool error = false;
00106 
00107     gnutls_certificate_free_ca_names( m_credentials );
00108 
00109     if( gnutls_certificate_verify_peers2( *m_session, &status ) < 0 )
00110       error = true;
00111 
00112     m_certInfo.status = 0;
00113     if( status & GNUTLS_CERT_INVALID )
00114       m_certInfo.status |= CertInvalid;
00115     if( status & GNUTLS_CERT_SIGNER_NOT_FOUND )
00116       m_certInfo.status |= CertSignerUnknown;
00117     if( status & GNUTLS_CERT_REVOKED )
00118       m_certInfo.status |= CertRevoked;
00119     if( status & GNUTLS_CERT_SIGNER_NOT_CA )
00120       m_certInfo.status |= CertSignerNotCa;
00121     const gnutls_datum_t* certList = 0;
00122     unsigned int certListSize;
00123     if( !error && ( ( certList = gnutls_certificate_get_peers( *m_session, &certListSize ) ) == 0 ) )
00124       error = true;
00125 
00126     gnutls_x509_crt_t* cert = new gnutls_x509_crt_t[certListSize+1];
00127     for( unsigned int i=0; !error && ( i<certListSize ); ++i )
00128     {
00129       if( gnutls_x509_crt_init( &cert[i] ) < 0
00130           || gnutls_x509_crt_import( cert[i], &certList[i], GNUTLS_X509_FMT_DER ) < 0 )
00131         error = true;
00132     }
00133 
00134     if( ( gnutls_x509_crt_check_issuer( cert[certListSize-1], cert[certListSize-1] ) > 0 )
00135          && certListSize > 0 )
00136       certListSize--;
00137 
00138     bool chain = true;
00139     for( unsigned int i=1; !error && ( i<certListSize ); ++i )
00140     {
00141       chain = error = !verifyAgainst( cert[i-1], cert[i] );
00142     }
00143     if( !chain )
00144       m_certInfo.status |= CertInvalid;
00145     m_certInfo.chain = chain;
00146 
00147     m_certInfo.chain = verifyAgainstCAs( cert[certListSize], 0 /*CAList*/, 0 /*CAListSize*/ );
00148 
00149     int t = (int)gnutls_x509_crt_get_activation_time( cert[0] );
00150     if( t == -1 )
00151       error = true;
00152     else if( t > time( 0 ) )
00153       m_certInfo.status |= CertNotActive;
00154     m_certInfo.date_from = t;
00155 
00156     t = (int)gnutls_x509_crt_get_expiration_time( cert[0] );
00157     if( t == -1 )
00158       error = true;
00159     else if( t < time( 0 ) )
00160       m_certInfo.status |= CertExpired;
00161     m_certInfo.date_to = t;
00162 
00163     char name[64];
00164     size_t nameSize = sizeof( name );
00165     gnutls_x509_crt_get_issuer_dn( cert[0], name, &nameSize );
00166     m_certInfo.issuer = name;
00167 
00168     nameSize = sizeof( name );
00169     gnutls_x509_crt_get_dn( cert[0], name, &nameSize );
00170     m_certInfo.server = name;
00171 
00172     const char* info;
00173     info = gnutls_compression_get_name( gnutls_compression_get( *m_session ) );
00174     if( info )
00175       m_certInfo.compression = info;
00176 
00177     info = gnutls_mac_get_name( gnutls_mac_get( *m_session ) );
00178     if( info )
00179       m_certInfo.mac = info;
00180 
00181     info = gnutls_cipher_get_name( gnutls_cipher_get( *m_session ) );
00182     if( info )
00183       m_certInfo.cipher = info;
00184 
00185     info = gnutls_protocol_get_name( gnutls_protocol_get_version( *m_session ) );
00186     if( info )
00187       m_certInfo.protocol = info;
00188 
00189     if( !gnutls_x509_crt_check_hostname( cert[0], m_server.c_str() ) )
00190       m_certInfo.status |= CertWrongPeer;
00191 
00192     for( unsigned int i = 0; i < certListSize; ++i )
00193       gnutls_x509_crt_deinit( cert[i] );
00194 
00195     delete[] cert;
00196 
00197     m_valid = true;
00198   }
00199 
00200   static bool verifyCert( gnutls_x509_crt_t cert, unsigned result )
00201   {
00202     return ! ( ( result & GNUTLS_CERT_INVALID )
00203       || gnutls_x509_crt_get_expiration_time( cert ) < time( 0 )
00204       || gnutls_x509_crt_get_activation_time( cert ) > time( 0 ) );
00205   }
00206 
00207   bool GnuTLSClient::verifyAgainst( gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer )
00208   {
00209     unsigned int result;
00210     gnutls_x509_crt_verify( cert, &issuer, 1, 0, &result );
00211     return verifyCert( cert, result );
00212   }
00213 
00214   bool GnuTLSClient::verifyAgainstCAs( gnutls_x509_crt_t cert, gnutls_x509_crt_t* CAList, int CAListSize )
00215   {
00216     unsigned int result;
00217     gnutls_x509_crt_verify( cert, CAList, CAListSize, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &result );
00218     return verifyCert( cert, result );
00219   }
00220 
00221 }
00222 
00223 #endif // HAVE_GNUTLS