gloox  1.0.27
tlsopensslbase.cpp
1 /*
2  Copyright (c) 2009-2023 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  if( m_cacerts.empty() )
130  SSL_CTX_set_default_verify_paths( m_ctx );
131 
132  StringList::const_iterator it = m_cacerts.begin();
133  for( ; it != m_cacerts.end(); ++it )
134  SSL_CTX_load_verify_locations( m_ctx, (*it).c_str(), 0 );
135  }
136 
137  void OpenSSLBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
138  {
139  m_clientKey = clientKey;
140  m_clientCerts = clientCerts;
141 
142  if( !m_clientKey.empty() && !m_clientCerts.empty() )
143  {
144  if( SSL_CTX_use_certificate_chain_file( m_ctx, m_clientCerts.c_str() ) != 1 )
145  {
146  // FIXME
147  }
148  if( SSL_CTX_use_RSAPrivateKey_file( m_ctx, m_clientKey.c_str(), SSL_FILETYPE_PEM ) != 1 )
149  {
150  // FIXME
151  }
152  }
153 
154  if ( SSL_CTX_check_private_key( m_ctx ) != 1 )
155  {
156  // FIXME
157  }
158  }
159 
161  {
162  if( !m_mutex.trylock() )
163  return;
164 
165  m_secure = false;
166  m_valid = false;
167 
168  m_mutex.unlock();
169  }
170 
171  void OpenSSLBase::doTLSOperation( TLSOperation op )
172  {
173  if( !m_handler )
174  return;
175 
176  int ret = 0;
177  bool onceAgain = false;
178 
179  do
180  {
181  switch( op )
182  {
183  case TLSHandshake:
184  ret = handshakeFunction();
185  break;
186  case TLSWrite:
187  ret = SSL_write( m_ssl, m_sendBuffer.c_str(), static_cast<int>( m_sendBuffer.length() ) );
188  break;
189  case TLSRead:
190  ret = SSL_read( m_ssl, m_buf, m_bufsize );
191  break;
192  }
193 
194  switch( SSL_get_error( m_ssl, ret ) )
195  {
196  case SSL_ERROR_WANT_READ:
197  case SSL_ERROR_WANT_WRITE:
198  pushFunc();
199  break;
200  case SSL_ERROR_NONE:
201  if( op == TLSHandshake )
202  m_secure = true;
203  else if( op == TLSWrite )
204  m_sendBuffer.erase( 0, ret );
205  else if( op == TLSRead )
206  m_handler->handleDecryptedData( this, std::string( m_buf, ret ) );
207  pushFunc();
208  break;
209  default:
210  if( !m_secure )
211  m_handler->handleHandshakeResult( this, false, m_certInfo );
212  return;
213  break;
214  }
215  if( !onceAgain && !m_recvBuffer.length() )
216  onceAgain = true;
217  else if( onceAgain )
218  onceAgain = false;
219  }
220  while( ( ( onceAgain || m_recvBuffer.length() ) && ( !m_secure || op == TLSRead ) )
221  || ( ( op == TLSWrite ) && ( ret > 0 ) ));
222  }
223 
224  int OpenSSLBase::ASN1Time2UnixTime( ASN1_TIME* time )
225  {
226  struct tm t;
227  const char* str = reinterpret_cast<const char*>( time->data );
228  size_t i = 0;
229 
230  memset( &t, 0, sizeof(t) );
231 
232  if( time->type == V_ASN1_UTCTIME ) /* two digit year */
233  {
234  t.tm_year = ( str[i++] - '0' ) * 10;
235  t.tm_year += ( str[i++] - '0' );
236 
237  if( t.tm_year < 70 )
238  t.tm_year += 100;
239  }
240  else if( time->type == V_ASN1_GENERALIZEDTIME ) /* four digit year */
241  {
242  t.tm_year = ( str[i++] - '0' ) * 1000;
243  t.tm_year += ( str[i++] - '0' ) * 100;
244  t.tm_year += ( str[i++] - '0' ) * 10;
245  t.tm_year += ( str[i++] - '0' );
246  t.tm_year -= 1900;
247  }
248 
249  t.tm_mon = ( str[i++] - '0' ) * 10;
250  t.tm_mon += ( str[i++] - '0' ) - 1; // -1 since January is 0 not 1.
251  t.tm_mday = ( str[i++] - '0' ) * 10;
252  t.tm_mday += ( str[i++] - '0' );
253  t.tm_hour = ( str[i++] - '0' ) * 10;
254  t.tm_hour += ( str[i++] - '0' );
255  t.tm_min = ( str[i++] - '0' ) * 10;
256  t.tm_min += ( str[i++] - '0' );
257  t.tm_sec = ( str[i++] - '0' ) * 10;
258  t.tm_sec += ( str[i++] - '0' );
259 
260  return static_cast<int>( mktime( &t ) );
261  }
262 
263 #if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER < 0x10100000 )
264  int SSL_SESSION_get_protocol_version( const SSL_SESSION* s )
265  {
266  return s->ssl_version;
267  }
268 #endif // OPENSSL_VERSION_NUMBER < 0x10100000
269 
271  {
272 
273  doTLSOperation( TLSHandshake );
274 
275  if( !m_secure )
276  return true;
277 
278  long res = SSL_get_verify_result( m_ssl );
279  if( res != X509_V_OK )
280  m_certInfo.status = CertInvalid;
281  else
282  m_certInfo.status = CertOk;
283 
284  X509* peer = SSL_get_peer_certificate( m_ssl );
285  if( peer )
286  {
287  char peer_CN[256];
288  X509_NAME_get_text_by_NID( X509_get_issuer_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
289  m_certInfo.issuer = peer_CN;
290  X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
291  m_certInfo.server = peer_CN;
292  m_certInfo.date_from = ASN1Time2UnixTime( X509_get_notBefore( peer ) );
293  m_certInfo.date_to = ASN1Time2UnixTime( X509_get_notAfter( peer ) );
294  std::string p( peer_CN );
295  std::transform( p.begin(), p.end(), p.begin(), tolower );
296 
297 #if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER >= 0x10002000 )
298  res = X509_check_host( peer, p.c_str(), p.length(), X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS, 0 );
299  if( res <= 0 ) // 0: verification failed; -1: internal error; -2 input is malformed
300  m_certInfo.status |= CertWrongPeer;
301 #else
302  if( p != m_server )
303  m_certInfo.status |= CertWrongPeer;
304 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000
305 
306  if( ASN1_UTCTIME_cmp_time_t( X509_get_notBefore( peer ), time( 0 ) ) != -1 )
307  m_certInfo.status |= CertNotActive;
308 
309  if( ASN1_UTCTIME_cmp_time_t( X509_get_notAfter( peer ), time( 0 ) ) != 1 )
310  m_certInfo.status |= CertExpired;
311 
312  X509_free( peer );
313  }
314  else
315  {
316  m_certInfo.status = CertInvalid;
317  }
318 
319  const char* tmp;
320  tmp = SSL_get_cipher_name( m_ssl );
321  if( tmp )
322  m_certInfo.cipher = tmp;
323 
324  SSL_SESSION* sess = SSL_get_session( m_ssl );
325  if( sess )
326  {
327  switch( SSL_SESSION_get_protocol_version( sess ) )
328  {
329  case TLS1_VERSION:
330  m_certInfo.protocol = "TLSv1.0";
331  break;
332  case TLS1_1_VERSION:
333  m_certInfo.protocol = "TLSv1.1";
334  break;
335  case TLS1_2_VERSION:
336  m_certInfo.protocol = "TLSv1.2";
337  break;
338  case TLS1_3_VERSION:
339  m_certInfo.protocol = "TLSv1.3";
340  break;
341  default:
342  m_certInfo.protocol = "Unknown TLS version";
343  break;
344  }
345  }
346 
347  tmp = SSL_COMP_get_name( SSL_get_current_compression( m_ssl ) );
348  if( tmp )
349  m_certInfo.compression = tmp;
350 
351  m_valid = true;
352 
353  m_handler->handleHandshakeResult( this, true, m_certInfo );
354  return true;
355  }
356 
357  void OpenSSLBase::pushFunc()
358  {
359  int wantwrite;
360  size_t wantread;
361  long frombio;
362  long tobio;
363 
364  while( ( wantwrite = BIO_pending( m_nbio ) ) > 0 )
365  {
366  if( wantwrite > m_bufsize )
367  wantwrite = m_bufsize;
368 
369  if( !wantwrite )
370  break;
371 
372  frombio = BIO_read( m_nbio, m_buf, wantwrite );
373 
374  if( m_handler )
375  m_handler->handleEncryptedData( this, std::string( m_buf, frombio ) );
376  }
377 
378  while( ( wantread = BIO_ctrl_get_read_request( m_nbio ) ) > 0 )
379  {
380  if( wantread > m_recvBuffer.length() )
381  wantread = m_recvBuffer.length();
382 
383  if( !wantread )
384  break;
385 
386  tobio = BIO_write( m_nbio, m_recvBuffer.c_str(), static_cast<int>( wantread ) );
387  m_recvBuffer.erase( 0, tobio );
388  }
389  }
390 
391 }
392 
393 #endif // HAVE_OPENSSL
virtual bool encrypt(const std::string &data)
virtual void setCACerts(const StringList &cacerts)
virtual bool handshake()
virtual void cleanup()
virtual void setClientCert(const std::string &clientKey, const std::string &clientCerts)
virtual bool init(const std::string &clientKey=EmptyString, const std::string &clientCerts=EmptyString, const StringList &cacerts=StringList())
OpenSSLBase(TLSHandler *th, const std::string &server=EmptyString)
virtual int decrypt(const std::string &data)
An abstract base class for TLS implementations.
Definition: tlsbase.h:32
An interface that allows for interacting with TLS implementations derived from TLSBase.
Definition: tlshandler.h:35
virtual void handleDecryptedData(const TLSBase *base, const std::string &data)=0
virtual void handleEncryptedData(const TLSBase *base, const std::string &data)=0
virtual void handleHandshakeResult(const TLSBase *base, bool success, CertInfo &certinfo)=0
The namespace for the gloox library.
Definition: adhoc.cpp:28
std::list< std::string > StringList
Definition: gloox.h:1251
@ CertExpired
Definition: gloox.h:979
@ CertOk
Definition: gloox.h:975
@ CertWrongPeer
Definition: gloox.h:981
@ CertInvalid
Definition: gloox.h:976
@ CertNotActive
Definition: gloox.h:980
std::string cipher
Definition: gloox.h:1002
std::string server
Definition: gloox.h:994
int date_from
Definition: gloox.h:995
std::string protocol
Definition: gloox.h:1001
std::string issuer
Definition: gloox.h:993
std::string compression
Definition: gloox.h:1004