gloox  1.0.9
tlsgnutlsclient.cpp
1 /*
2  Copyright (c) 2005-2013 by Jakob Schroeter <js@camaya.net>
3  This file is part of the gloox library. http://camaya.net/gloox
4 
5  This software is distributed under a license. The full license
6  agreement can be found in the file LICENSE in this distribution.
7  This software may not be copied, modified, sold or distributed
8  other than expressed in the named license agreement.
9 
10  This software is distributed without any warranty.
11 */
12 
13 
14 
15 #include "tlsgnutlsclient.h"
16 
17 #ifdef HAVE_GNUTLS
18 
19 #include <errno.h>
20 
21 #ifdef HAVE_PTHREAD
22 extern "C" {
23 GCRY_THREAD_OPTION_PTHREAD_IMPL;
24 }
25 #endif
26 
27 namespace gloox
28 {
29 
30  GnuTLSClient::GnuTLSClient( TLSHandler* th, const std::string& server )
31  : GnuTLSBase( th, server )
32  {
33  }
34 
36  {
37  }
38 
40  {
42  if( m_credentials )
43  gnutls_certificate_free_credentials( m_credentials );
44  init();
45  }
46 
47  bool GnuTLSClient::init( const std::string& /*clientKey*/,
48  const std::string& /*clientCerts*/,
49  const StringList& /*cacerts*/ )
50  {
51  gcry_control( GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread );
52 
53  const int protocolPriority[] = {
54 #ifdef GNUTLS_TLS1_2
55  GNUTLS_TLS1_2,
56 #endif
57  GNUTLS_TLS1_1, GNUTLS_TLS1, 0 };
58  const int kxPriority[] = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_DHE_DSS, 0 };
59  const int cipherPriority[] = { GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_CBC,
60  GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_ARCFOUR, 0 };
61  const int compPriority[] = { GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 };
62  const int macPriority[] = { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 };
63 
64  if( m_initLib && gnutls_global_init() != 0 )
65  return false;
66 
67  if( gnutls_certificate_allocate_credentials( &m_credentials ) < 0 )
68  return false;
69 
70  if( gnutls_init( m_session, GNUTLS_CLIENT ) != 0 )
71  {
72  gnutls_certificate_free_credentials( m_credentials );
73  return false;
74  }
75 
76  gnutls_protocol_set_priority( *m_session, protocolPriority );
77  gnutls_cipher_set_priority( *m_session, cipherPriority );
78  gnutls_compression_set_priority( *m_session, compPriority );
79  gnutls_kx_set_priority( *m_session, kxPriority );
80  gnutls_mac_set_priority( *m_session, macPriority );
81  gnutls_credentials_set( *m_session, GNUTLS_CRD_CERTIFICATE, m_credentials );
82 
83  gnutls_transport_set_ptr( *m_session, (gnutls_transport_ptr_t)this );
84  gnutls_transport_set_push_function( *m_session, pushFunc );
85  gnutls_transport_set_pull_function( *m_session, pullFunc );
86 
87  m_valid = true;
88  return true;
89  }
90 
91  void GnuTLSClient::setCACerts( const StringList& cacerts )
92  {
93  m_cacerts = cacerts;
94 
95  StringList::const_iterator it = m_cacerts.begin();
96  for( ; it != m_cacerts.end(); ++it )
97  gnutls_certificate_set_x509_trust_file( m_credentials, (*it).c_str(), GNUTLS_X509_FMT_PEM );
98  }
99 
100  void GnuTLSClient::setClientCert( const std::string& clientKey, const std::string& clientCerts )
101  {
102  m_clientKey = clientKey;
103  m_clientCerts = clientCerts;
104 
105  if( !m_clientKey.empty() && !m_clientCerts.empty() )
106  {
107  gnutls_certificate_set_x509_key_file( m_credentials, m_clientCerts.c_str(),
108  m_clientKey.c_str(), GNUTLS_X509_FMT_PEM );
109  }
110  }
111 
112  void GnuTLSClient::getCertInfo()
113  {
114  unsigned int status;
115  bool error = false;
116 
117  gnutls_certificate_free_ca_names( m_credentials );
118 
119  if( gnutls_certificate_verify_peers2( *m_session, &status ) < 0 )
120  error = true;
121 
122  m_certInfo.status = 0;
123  if( status & GNUTLS_CERT_INVALID )
124  m_certInfo.status |= CertInvalid;
125  if( status & GNUTLS_CERT_SIGNER_NOT_FOUND )
126  m_certInfo.status |= CertSignerUnknown;
127  if( status & GNUTLS_CERT_REVOKED )
128  m_certInfo.status |= CertRevoked;
129  if( status & GNUTLS_CERT_SIGNER_NOT_CA )
130  m_certInfo.status |= CertSignerNotCa;
131  const gnutls_datum_t* certList = 0;
132  unsigned int certListSize = 0;
133  if( !error && ( ( certList = gnutls_certificate_get_peers( *m_session, &certListSize ) ) == 0 ) )
134  error = true;
135 
136  unsigned int certListSizeFull = certListSize;
137 
138  gnutls_x509_crt_t* cert = new gnutls_x509_crt_t[certListSize+1];
139  for( unsigned int i=0; !error && ( i<certListSize ); ++i )
140  {
141  if( gnutls_x509_crt_init( &cert[i] ) < 0
142  || gnutls_x509_crt_import( cert[i], &certList[i], GNUTLS_X509_FMT_DER ) < 0 )
143  error = true;
144  }
145 
146  if( ( gnutls_x509_crt_check_issuer( cert[certListSize-1], cert[certListSize-1] ) > 0 )
147  && certListSize > 0 )
148  certListSize--;
149 
150  bool chain = true;
151  for( unsigned int i=1; !error && ( i<certListSize ); ++i )
152  {
153  chain = error = !verifyAgainst( cert[i-1], cert[i] );
154  }
155  if( !chain )
156  m_certInfo.status |= CertInvalid;
157  m_certInfo.chain = chain;
158 
159  m_certInfo.chain = verifyAgainstCAs( cert[certListSize], 0 /*CAList*/, 0 /*CAListSize*/ );
160 
161  int t = (int)gnutls_x509_crt_get_activation_time( cert[0] );
162  if( t == -1 )
163  error = true;
164  else if( t > time( 0 ) )
165  m_certInfo.status |= CertNotActive;
166  m_certInfo.date_from = t;
167 
168  t = (int)gnutls_x509_crt_get_expiration_time( cert[0] );
169  if( t == -1 )
170  error = true;
171  else if( t < time( 0 ) )
172  m_certInfo.status |= CertExpired;
173  m_certInfo.date_to = t;
174 
175  char name[64];
176  size_t nameSize = sizeof( name );
177  gnutls_x509_crt_get_issuer_dn( cert[0], name, &nameSize );
178  m_certInfo.issuer = name;
179 
180  nameSize = sizeof( name );
181  gnutls_x509_crt_get_dn( cert[0], name, &nameSize );
182  m_certInfo.server = name;
183 
184  const char* info;
185  info = gnutls_compression_get_name( gnutls_compression_get( *m_session ) );
186  if( info )
187  m_certInfo.compression = info;
188 
189  info = gnutls_mac_get_name( gnutls_mac_get( *m_session ) );
190  if( info )
191  m_certInfo.mac = info;
192 
193  info = gnutls_cipher_get_name( gnutls_cipher_get( *m_session ) );
194  if( info )
195  m_certInfo.cipher = info;
196 
197  info = gnutls_protocol_get_name( gnutls_protocol_get_version( *m_session ) );
198  if( info )
199  m_certInfo.protocol = info;
200 
201  if( !gnutls_x509_crt_check_hostname( cert[0], m_server.c_str() ) )
202  m_certInfo.status |= CertWrongPeer;
203 
204  for( unsigned int i = 0; i < certListSizeFull; ++i )
205  gnutls_x509_crt_deinit( cert[i] );
206 
207  delete[] cert;
208 
209  m_valid = true;
210  }
211 
212  static bool verifyCert( gnutls_x509_crt_t cert, unsigned result )
213  {
214  return ! ( ( result & GNUTLS_CERT_INVALID )
215  || gnutls_x509_crt_get_expiration_time( cert ) < time( 0 )
216  || gnutls_x509_crt_get_activation_time( cert ) > time( 0 ) );
217  }
218 
219  bool GnuTLSClient::verifyAgainst( gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer )
220  {
221  unsigned int result;
222  gnutls_x509_crt_verify( cert, &issuer, 1, 0, &result );
223  return verifyCert( cert, result );
224  }
225 
226  bool GnuTLSClient::verifyAgainstCAs( gnutls_x509_crt_t cert, gnutls_x509_crt_t* CAList, int CAListSize )
227  {
228  unsigned int result;
229  gnutls_x509_crt_verify( cert, CAList, CAListSize, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &result );
230  return verifyCert( cert, result );
231  }
232 
233 }
234 
235 #endif // HAVE_GNUTLS