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