gloox  1.0.20
tlsopensslbase.cpp
1 /*
2  Copyright (c) 2009-2017 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 #include <string.h>
29 
30 namespace gloox
31 {
32 
33  OpenSSLBase::OpenSSLBase( TLSHandler* th, const std::string& server )
34  : TLSBase( th, server ), m_ssl( 0 ), m_ctx( 0 ), m_buf( 0 ), m_bufsize( 17000 )
35  {
36  m_buf = static_cast<char*>( calloc( m_bufsize + 1, sizeof( char ) ) );
37  }
38 
40  {
41  m_handler = 0;
42  free( m_buf );
43  SSL_CTX_free( m_ctx );
44  SSL_shutdown( m_ssl );
45  SSL_free( m_ssl );
46  BIO_free( m_nbio );
47  cleanup();
48  }
49 
50  bool OpenSSLBase::init( const std::string& clientKey,
51  const std::string& clientCerts,
52  const StringList& cacerts )
53  {
54 #if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER < 0x10100000 )
55  if( m_initLib )
56  SSL_library_init();
57 #endif // OPENSSL_VERSION_NUMBER < 0x10100000
58 
59  SSL_COMP_add_compression_method( 193, COMP_zlib() );
60 
61  OpenSSL_add_all_algorithms();
62 
63  if( !setType() ) //inits m_ctx
64  return false;
65 
66  setClientCert( clientKey, clientCerts );
67  setCACerts( cacerts );
68 
69  if( !SSL_CTX_set_cipher_list( m_ctx, "HIGH:MEDIUM:AES:@STRENGTH" ) )
70  return false;
71 
72  m_ssl = SSL_new( m_ctx );
73  if( !m_ssl )
74  return false;
75 
76  if( !BIO_new_bio_pair( &m_ibio, 0, &m_nbio, 0 ) )
77  return false;
78 
79  SSL_set_bio( m_ssl, m_ibio, m_ibio );
80  SSL_set_mode( m_ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE );
81 
82  ERR_load_crypto_strings();
83  SSL_load_error_strings();
84 
85  if( !privateInit() )
86  return false;
87 
88  m_valid = true;
89  return true;
90  }
91 
92  bool OpenSSLBase::encrypt( const std::string& data )
93  {
94  m_sendBuffer += data;
95 
96  if( !m_secure )
97  {
98  handshake();
99  return 0;
100  }
101 
102  doTLSOperation( TLSWrite );
103  return true;
104  }
105 
106  int OpenSSLBase::decrypt( const std::string& data )
107  {
108  m_recvBuffer += data;
109 
110  if( !m_secure )
111  {
112  handshake();
113  return 0;
114  }
115 
116  doTLSOperation( TLSRead );
117  return true;
118  }
119 
120  void OpenSSLBase::setCACerts( const StringList& cacerts )
121  {
122  m_cacerts = cacerts;
123 
124  StringList::const_iterator it = m_cacerts.begin();
125  for( ; it != m_cacerts.end(); ++it )
126  SSL_CTX_load_verify_locations( m_ctx, (*it).c_str(), 0 );
127  }
128 
129  void OpenSSLBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
130  {
131  m_clientKey = clientKey;
132  m_clientCerts = clientCerts;
133 
134  if( !m_clientKey.empty() && !m_clientCerts.empty() )
135  {
136  if( SSL_CTX_use_certificate_chain_file( m_ctx, m_clientCerts.c_str() ) != 1 )
137  {
138  // FIXME
139  }
140  if( SSL_CTX_use_RSAPrivateKey_file( m_ctx, m_clientKey.c_str(), SSL_FILETYPE_PEM ) != 1 )
141  {
142  // FIXME
143  }
144  }
145 
146  if ( SSL_CTX_check_private_key( m_ctx ) != 1 )
147  {
148  // FIXME
149  }
150  }
151 
153  {
154  if( !m_mutex.trylock() )
155  return;
156 
157  m_secure = false;
158  m_valid = false;
159 
160  m_mutex.unlock();
161  }
162 
163  void OpenSSLBase::doTLSOperation( TLSOperation op )
164  {
165  if( !m_handler )
166  return;
167 
168  int ret = 0;
169  bool onceAgain = false;
170 
171  do
172  {
173  switch( op )
174  {
175  case TLSHandshake:
176  ret = handshakeFunction();
177  break;
178  case TLSWrite:
179  ret = SSL_write( m_ssl, m_sendBuffer.c_str(), static_cast<int>( m_sendBuffer.length() ) );
180  break;
181  case TLSRead:
182  ret = SSL_read( m_ssl, m_buf, m_bufsize );
183  break;
184  }
185 
186  switch( SSL_get_error( m_ssl, ret ) )
187  {
188  case SSL_ERROR_WANT_READ:
189  case SSL_ERROR_WANT_WRITE:
190  pushFunc();
191  break;
192  case SSL_ERROR_NONE:
193  if( op == TLSHandshake )
194  m_secure = true;
195  else if( op == TLSWrite )
196  m_sendBuffer.erase( 0, ret );
197  else if( op == TLSRead )
198  m_handler->handleDecryptedData( this, std::string( m_buf, ret ) );
199  pushFunc();
200  break;
201  default:
202  if( !m_secure )
203  m_handler->handleHandshakeResult( this, false, m_certInfo );
204  return;
205  break;
206  }
207  if( !onceAgain && !m_recvBuffer.length() )
208  onceAgain = true;
209  else if( onceAgain )
210  onceAgain = false;
211  }
212  while( ( ( onceAgain || m_recvBuffer.length() ) && ( !m_secure || op == TLSRead ) )
213  || ( ( op == TLSWrite ) && ( ret > 0 ) ));
214  }
215 
216  int OpenSSLBase::ASN1Time2UnixTime( ASN1_TIME* time )
217  {
218  struct tm t;
219  const char* str = reinterpret_cast<const char*>( time->data );
220  size_t i = 0;
221 
222  memset( &t, 0, sizeof(t) );
223 
224  if( time->type == V_ASN1_UTCTIME ) /* two digit year */
225  {
226  t.tm_year = ( str[i++] - '0' ) * 10;
227  t.tm_year += ( str[i++] - '0' );
228 
229  if( t.tm_year < 70 )
230  t.tm_year += 100;
231  }
232  else if( time->type == V_ASN1_GENERALIZEDTIME ) /* four digit year */
233  {
234  t.tm_year = ( str[i++] - '0' ) * 1000;
235  t.tm_year += ( str[i++] - '0' ) * 100;
236  t.tm_year += ( str[i++] - '0' ) * 10;
237  t.tm_year += ( str[i++] - '0' );
238  t.tm_year -= 1900;
239  }
240 
241  t.tm_mon = ( str[i++] - '0' ) * 10;
242  t.tm_mon += ( str[i++] - '0' ) - 1; // -1 since January is 0 not 1.
243  t.tm_mday = ( str[i++] - '0' ) * 10;
244  t.tm_mday += ( str[i++] - '0' );
245  t.tm_hour = ( str[i++] - '0' ) * 10;
246  t.tm_hour += ( str[i++] - '0' );
247  t.tm_min = ( str[i++] - '0' ) * 10;
248  t.tm_min += ( str[i++] - '0' );
249  t.tm_sec = ( str[i++] - '0' ) * 10;
250  t.tm_sec += ( str[i++] - '0' );
251 
252  return static_cast<int>( mktime( &t ) );
253  }
254 
255 #if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER < 0x10100000 )
256  int SSL_SESSION_get_protocol_version( const SSL_SESSION* s )
257  {
258  return s->ssl_version;
259  }
260 #endif // OPENSSL_VERSION_NUMBER < 0x10100000
261 
263  {
264 
265  doTLSOperation( TLSHandshake );
266 
267  if( !m_secure )
268  return true;
269 
270  long res = SSL_get_verify_result( m_ssl );
271  if( res != X509_V_OK )
272  m_certInfo.status = CertInvalid;
273  else
274  m_certInfo.status = CertOk;
275 
276  X509* peer = SSL_get_peer_certificate( m_ssl );
277  if( peer )
278  {
279  char peer_CN[256];
280  X509_NAME_get_text_by_NID( X509_get_issuer_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
281  m_certInfo.issuer = peer_CN;
282  X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
283  m_certInfo.server = peer_CN;
284  m_certInfo.date_from = ASN1Time2UnixTime( X509_get_notBefore( peer ) );
285  m_certInfo.date_to = ASN1Time2UnixTime( X509_get_notAfter( peer ) );
286  std::string p( peer_CN );
287  std::transform( p.begin(), p.end(), p.begin(), tolower );
288 
289 #if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER >= 0x10002000 )
290  res = X509_check_host( peer, p.c_str(), p.length(), X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS, 0 );
291  if( res <= 0 ) // 0: verification failed; -1: internal error; -2 input is malformed
292  m_certInfo.status |= CertWrongPeer;
293 #else
294  if( p != m_server )
295  m_certInfo.status |= CertWrongPeer;
296 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000
297 
298  if( ASN1_UTCTIME_cmp_time_t( X509_get_notBefore( peer ), time( 0 ) ) != -1 )
299  m_certInfo.status |= CertNotActive;
300 
301  if( ASN1_UTCTIME_cmp_time_t( X509_get_notAfter( peer ), time( 0 ) ) != 1 )
302  m_certInfo.status |= CertExpired;
303 
304  X509_free( peer );
305  }
306  else
307  {
308  m_certInfo.status = CertInvalid;
309  }
310 
311  const char* tmp;
312  tmp = SSL_get_cipher_name( m_ssl );
313  if( tmp )
314  m_certInfo.cipher = tmp;
315 
316  SSL_SESSION* sess = SSL_get_session( m_ssl );
317  if( sess )
318  {
319  switch( SSL_SESSION_get_protocol_version( sess ) )
320  {
321  case TLS1_VERSION:
322  m_certInfo.protocol = "TLSv1";
323  break;
324  case TLS1_1_VERSION:
325  m_certInfo.protocol = "TLSv1.1";
326  break;
327  case TLS1_2_VERSION:
328  m_certInfo.protocol = "TLSv1.2";
329  break;
330 #ifdef TLS1_3_VERSION
331  case TLS1_3_VERSION:
332  m_certInfo.protocol = "TLSv1.3";
333  break;
334 #endif // TLS1_3_VERSION
335  default:
336  m_certInfo.protocol = "Unknown TLS version";
337  break;
338  }
339  }
340 
341  tmp = SSL_COMP_get_name( SSL_get_current_compression( m_ssl ) );
342  if( tmp )
343  m_certInfo.compression = tmp;
344 
345  m_valid = true;
346 
347  m_handler->handleHandshakeResult( this, true, m_certInfo );
348  return true;
349  }
350 
351  void OpenSSLBase::pushFunc()
352  {
353  int wantwrite;
354  size_t wantread;
355  long frombio;
356  long tobio;
357 
358  while( ( wantwrite = BIO_pending( m_nbio ) ) > 0 )
359  {
360  if( wantwrite > m_bufsize )
361  wantwrite = m_bufsize;
362 
363  if( !wantwrite )
364  break;
365 
366  frombio = BIO_read( m_nbio, m_buf, wantwrite );
367 
368  if( m_handler )
369  m_handler->handleEncryptedData( this, std::string( m_buf, frombio ) );
370  }
371 
372  while( ( wantread = BIO_ctrl_get_read_request( m_nbio ) ) > 0 )
373  {
374  if( wantread > m_recvBuffer.length() )
375  wantread = m_recvBuffer.length();
376 
377  if( !wantread )
378  break;
379 
380  tobio = BIO_write( m_nbio, m_recvBuffer.c_str(), static_cast<int>( wantread ) );
381  m_recvBuffer.erase( 0, tobio );
382  }
383  }
384 
385 }
386 
387 #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:1251
virtual void handleEncryptedData(const TLSBase *base, const std::string &data)=0
std::string cipher
Definition: gloox.h:1002
virtual int decrypt(const std::string &data)
std::string issuer
Definition: gloox.h:993
std::string server
Definition: gloox.h:994
virtual void setCACerts(const StringList &cacerts)
The namespace for the gloox library.
Definition: adhoc.cpp:27
int date_from
Definition: gloox.h:995
virtual bool encrypt(const std::string &data)
std::string protocol
Definition: gloox.h:1001
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:1004