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