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