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