gloox  1.0
tlsopensslbase.cpp
1 /*
2  Copyright (c) 2009 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 "tlsopensslbase.h"
16 
17 #ifdef HAVE_OPENSSL
18 
19 #include <algorithm>
20 #include <cctype>
21 #include <ctime>
22 #include <cstdlib>
23 
24 #include <openssl/err.h>
25 
26 namespace gloox
27 {
28 
29  OpenSSLBase::OpenSSLBase( TLSHandler* th, const std::string& server )
30  : TLSBase( th, server ), m_ssl( 0 ), m_ctx( 0 ), m_buf( 0 ), m_bufsize( 17000 )
31  {
32  m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) );
33  }
34 
36  {
37  m_handler = 0;
38  free( m_buf );
39  SSL_CTX_free( m_ctx );
40  SSL_shutdown( m_ssl );
41  SSL_free( m_ssl );
42  BIO_free( m_nbio );
43  cleanup();
44  }
45 
46  bool OpenSSLBase::init( const std::string& clientKey,
47  const std::string& clientCerts,
48  const StringList& cacerts )
49  {
50  if( m_initLib )
51  SSL_library_init();
52 
53  SSL_COMP_add_compression_method( 193, COMP_zlib() );
54 
55  OpenSSL_add_all_algorithms();
56 
57  if( !setType() ) //inits m_ctx
58  return false;
59 
60  setClientCert( clientKey, clientCerts );
61  setCACerts( cacerts );
62 
63  if( !SSL_CTX_set_cipher_list( m_ctx, "HIGH:MEDIUM:AES:@STRENGTH" ) )
64  return false;
65 
66  m_ssl = SSL_new( m_ctx );
67  if( !m_ssl )
68  return false;
69 
70  if( !BIO_new_bio_pair( &m_ibio, 0, &m_nbio, 0 ) )
71  return false;
72 
73  SSL_set_bio( m_ssl, m_ibio, m_ibio );
74  SSL_set_mode( m_ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE );
75 
76  ERR_load_crypto_strings();
77  SSL_load_error_strings();
78 
79  if( !privateInit() )
80  return false;
81 
82  m_valid = true;
83  return true;
84  }
85 
86  bool OpenSSLBase::encrypt( const std::string& data )
87  {
88  m_sendBuffer += data;
89 
90  if( !m_secure )
91  {
92  handshake();
93  return 0;
94  }
95 
96  doTLSOperation( TLSWrite );
97  return true;
98  }
99 
100  int OpenSSLBase::decrypt( const std::string& data )
101  {
102  m_recvBuffer += data;
103 
104  if( !m_secure )
105  {
106  handshake();
107  return 0;
108  }
109 
110  doTLSOperation( TLSRead );
111  return true;
112  }
113 
114  void OpenSSLBase::setCACerts( const StringList& cacerts )
115  {
116  m_cacerts = cacerts;
117 
118  StringList::const_iterator it = m_cacerts.begin();
119  for( ; it != m_cacerts.end(); ++it )
120  SSL_CTX_load_verify_locations( m_ctx, (*it).c_str(), 0 );
121  }
122 
123  void OpenSSLBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
124  {
125  m_clientKey = clientKey;
126  m_clientCerts = clientCerts;
127 
128  if( !m_clientKey.empty() && !m_clientCerts.empty() )
129  {
130  if( SSL_CTX_use_certificate_chain_file( m_ctx, m_clientCerts.c_str() ) != 1 )
131  {
132  // FIXME
133  }
134  if( SSL_CTX_use_RSAPrivateKey_file( m_ctx, m_clientKey.c_str(), SSL_FILETYPE_PEM ) != 1 )
135  {
136  // FIXME
137  }
138  }
139 
140  if ( SSL_CTX_check_private_key( m_ctx ) != 1 )
141  {
142  // FIXME
143  }
144  }
145 
147  {
148  if( !m_mutex.trylock() )
149  return;
150 
151  m_secure = false;
152  m_valid = false;
153 
154  m_mutex.unlock();
155  }
156 
157  void OpenSSLBase::doTLSOperation( TLSOperation op )
158  {
159  if( !m_handler )
160  return;
161 
162  int ret = 0;
163  bool onceAgain = false;
164 
165  do
166  {
167  switch( op )
168  {
169  case TLSHandshake:
170  ret = handshakeFunction();
171  break;
172  case TLSWrite:
173  ret = SSL_write( m_ssl, m_sendBuffer.c_str(), m_sendBuffer.length() );
174  break;
175  case TLSRead:
176  ret = SSL_read( m_ssl, m_buf, m_bufsize );
177  break;
178  }
179 
180  switch( SSL_get_error( m_ssl, ret ) )
181  {
182  case SSL_ERROR_WANT_READ:
183  case SSL_ERROR_WANT_WRITE:
184  pushFunc();
185  break;
186  case SSL_ERROR_NONE:
187  if( op == TLSHandshake )
188  m_secure = true;
189  else if( op == TLSWrite )
190  m_sendBuffer.erase( 0, ret );
191  else if( op == TLSRead )
192  m_handler->handleDecryptedData( this, std::string( m_buf, ret ) );
193  pushFunc();
194  break;
195  default:
196  if( !m_secure )
197  m_handler->handleHandshakeResult( this, false, m_certInfo );
198  return;
199  break;
200  }
201  if( !onceAgain && !m_recvBuffer.length() )
202  onceAgain = true;
203  else if( onceAgain )
204  onceAgain = false;
205  }
206  while( ( ( onceAgain || m_recvBuffer.length() ) && ( !m_secure || op == TLSRead ) )
207  || ( ( op == TLSWrite ) && ( ret > 0 ) ));
208  }
209 
210  int OpenSSLBase::openSSLTime2UnixTime( const char* time_string )
211  {
212  char tstring[19];
213 
214  // making seperate c string out of time string
215  int m = 0;
216  for( int n = 0; n < 12; n += 2 )
217  {
218  tstring[m] = time_string[n];
219  tstring[m + 1] = time_string[n + 1];
220  tstring[m + 2] = 0;
221  m += 3;
222  }
223 
224  // converting to struct tm
225  tm time_st;
226  time_st.tm_year = ( atoi( &tstring[3 * 0] ) >= 70 ) ? atoi( &tstring[3 * 0] )
227  : atoi( &tstring[3 * 0] ) + 100;
228  time_st.tm_mon = atoi( &tstring[3 * 1] ) - 1;
229  time_st.tm_mday = atoi( &tstring[3 * 2] );
230  time_st.tm_hour = atoi( &tstring[3 * 3] );
231  time_st.tm_min = atoi( &tstring[3 * 4] );
232  time_st.tm_sec = atoi( &tstring[3 * 5] );
233 
234  time_t unixt = mktime( &time_st );
235  return unixt;
236  }
237 
239  {
240 
241  doTLSOperation( TLSHandshake );
242 
243  if( !m_secure )
244  return true;
245 
246  int res = SSL_get_verify_result( m_ssl );
247  if( res != X509_V_OK )
248  m_certInfo.status = CertInvalid;
249  else
250  m_certInfo.status = CertOk;
251 
252  X509* peer = SSL_get_peer_certificate( m_ssl );
253  if( peer )
254  {
255  char peer_CN[256];
256  X509_NAME_get_text_by_NID( X509_get_issuer_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
257  m_certInfo.issuer = peer_CN;
258  X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
259  m_certInfo.server = peer_CN;
260  m_certInfo.date_from = openSSLTime2UnixTime( (char*) (peer->cert_info->validity->notBefore->data) );
261  m_certInfo.date_to = openSSLTime2UnixTime( (char*) (peer->cert_info->validity->notAfter->data) );
262  std::string p( peer_CN );
263  std::transform( p.begin(), p.end(), p.begin(), tolower );
264  if( p != m_server )
265  m_certInfo.status |= CertWrongPeer;
266 
267  if( ASN1_UTCTIME_cmp_time_t( X509_get_notBefore( peer ), time( 0 ) ) != -1 )
268  m_certInfo.status |= CertNotActive;
269 
270  if( ASN1_UTCTIME_cmp_time_t( X509_get_notAfter( peer ), time( 0 ) ) != 1 )
271  m_certInfo.status |= CertExpired;
272  }
273  else
274  {
275  m_certInfo.status = CertInvalid;
276  }
277 
278  const char* tmp;
279  tmp = SSL_get_cipher_name( m_ssl );
280  if( tmp )
281  m_certInfo.cipher = tmp;
282 
283  tmp = SSL_get_cipher_version( m_ssl );
284  if( tmp )
285  m_certInfo.protocol = tmp;
286 
287  tmp = SSL_COMP_get_name( SSL_get_current_compression( m_ssl ) );
288  if( tmp )
289  m_certInfo.compression = tmp;
290 
291  m_valid = true;
292 
293  m_handler->handleHandshakeResult( this, true, m_certInfo );
294  return true;
295  }
296 
297  void OpenSSLBase::pushFunc()
298  {
299  int wantwrite;
300  size_t wantread;
301  int frombio;
302  int tobio;
303 
304  while( ( wantwrite = BIO_ctrl_pending( m_nbio ) ) > 0 )
305  {
306  if( wantwrite > m_bufsize )
307  wantwrite = m_bufsize;
308 
309  if( !wantwrite )
310  break;
311 
312  frombio = BIO_read( m_nbio, m_buf, wantwrite );
313 
314  if( m_handler )
315  m_handler->handleEncryptedData( this, std::string( m_buf, frombio ) );
316  }
317 
318  while( ( wantread = BIO_ctrl_get_read_request( m_nbio ) ) > 0 )
319  {
320  if( wantread > m_recvBuffer.length() )
321  wantread = m_recvBuffer.length();
322 
323  if( !wantread )
324  break;
325 
326  tobio = BIO_write( m_nbio, m_recvBuffer.c_str(), wantread );
327  m_recvBuffer.erase( 0, tobio );
328  }
329  }
330 
331 }
332 
333 #endif // HAVE_OPENSSL