gloox  1.0.27
tlsgnutlsbase.cpp
1 /*
2  Copyright (c) 2005-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 "tlsgnutlsbase.h"
16 
17 #ifdef HAVE_GNUTLS
18 
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <cstdio>
23 
24 namespace gloox
25 {
26 
27  GnuTLSBase::GnuTLSBase( TLSHandler* th, const std::string& server )
28  : TLSBase( th, server ), m_session( new gnutls_session_t ), m_buf( 0 ), m_bufsize( 17000 )
29  {
30  m_buf = static_cast<char*>( calloc( m_bufsize + 1, sizeof( char ) ) );
31  }
32 
34  {
35  free( m_buf );
36  m_buf = 0;
37  cleanup();
38  delete m_session;
39 // FIXME: It segfaults if more then one account uses
40 // encryption at same time, so we comment it for now.
41 // Do we need to deinit at all?
42 // gnutls_global_deinit();
43  }
44 
45  bool GnuTLSBase::encrypt( const std::string& data )
46  {
47  if( !m_secure )
48  {
49  handshake();
50  return true;
51  }
52 
53  ssize_t ret = 0;
54  std::string::size_type sum = 0;
55  do
56  {
57  ret = gnutls_record_send( *m_session, data.c_str() + sum, data.length() - sum );
58  sum += ret;
59  }
60  while( ( ret == GNUTLS_E_AGAIN ) || ( ret == GNUTLS_E_INTERRUPTED ) || sum < data.length() );
61  return true;
62  }
63 
64  int GnuTLSBase::decrypt( const std::string& data )
65  {
66  m_recvBuffer += data;
67 
68  if( !m_secure )
69  {
70  handshake();
71  return static_cast<int>( data.length() );
72  }
73 
74  int sum = 0;
75  int ret = 0;
76  bool stop = false;
77  do
78  {
79  if( ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
80  stop = true;
81  else
82  stop = false;
83 
84  ret = static_cast<int>( gnutls_record_recv( *m_session, m_buf, m_bufsize ) );
85  if( ret > 0 && m_handler )
86  {
87  m_handler->handleDecryptedData( this, std::string( m_buf, ret ) );
88  sum += ret;
89  }
90  if( stop )
91  break;
92  }
93  while( ret > 0 || ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED );
94 
95  return sum;
96  }
97 
99  {
100  if( !m_mutex.trylock() )
101  return;
102 
103  TLSHandler* handler = m_handler;
104  m_handler = 0;
105  gnutls_bye( *m_session, GNUTLS_SHUT_RDWR );
106  gnutls_db_remove_session( *m_session );
107  gnutls_credentials_clear( *m_session );
108  if( m_session )
109  gnutls_deinit( *m_session );
110 
111  delete m_session;
112 
113  m_secure = false;
114  m_valid = false;
115  m_session = 0;
116  m_session = new gnutls_session_t;
117  m_handler = handler;
118 
119  m_mutex.unlock();
120  }
121 
123  {
124  if( !m_handler )
125  return false;
126 
127  int ret = gnutls_handshake( *m_session );
128  if( ret < 0 && gnutls_error_is_fatal( ret ) )
129  {
130  gnutls_perror( ret );
131  gnutls_db_remove_session( *m_session );
132  m_valid = false;
133 
134  m_handler->handleHandshakeResult( this, false, m_certInfo );
135  return false;
136  }
137  else if( ret == GNUTLS_E_AGAIN )
138  {
139  return true;
140  }
141 
142  m_secure = true;
143 
144  getCertInfo();
145 
146  m_handler->handleHandshakeResult( this, true, m_certInfo );
147  return true;
148  }
149 
151  {
152 #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
153  return true;
154 #else
155  return false;
156 #endif
157  }
158 
159  const std::string GnuTLSBase::channelBinding() const
160  {
161 #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
162  gnutls_datum_t cb;
163  int rc;
164  rc = gnutls_session_channel_binding( *m_session,
165 #ifdef GNUTLS_CB_TLS_EXPORTER
166  ( m_certInfo.protocol == "TLSv1.3" ) ? GNUTLS_CB_TLS_EXPORTER : GNUTLS_CB_TLS_UNIQUE,
167 #else
168  GNUTLS_CB_TLS_UNIQUE,
169 #endif
170  &cb );
171  if( !rc )
172  return std::string( reinterpret_cast<char*>( cb.data ), cb.size );
173  else
174 #endif
175  return EmptyString;
176  }
177 
178  ssize_t GnuTLSBase::pullFunc( void* data, size_t len )
179  {
180  ssize_t cpy = ( len > m_recvBuffer.length() ) ? ( m_recvBuffer.length() ) : ( len );
181  if( cpy > 0 )
182  {
183  memcpy( data, static_cast<const void*>( m_recvBuffer.c_str() ), cpy );
184  m_recvBuffer.erase( 0, cpy );
185  return cpy;
186  }
187  else
188  {
189  errno = EAGAIN;
190  return GNUTLS_E_AGAIN;
191  }
192  }
193 
194  ssize_t GnuTLSBase::pullFunc( gnutls_transport_ptr_t ptr, void* data, size_t len )
195  {
196  return static_cast<GnuTLSBase*>( ptr )->pullFunc( data, len );
197  }
198 
199  ssize_t GnuTLSBase::pushFunc( const void* data, size_t len )
200  {
201  if( m_handler )
202  m_handler->handleEncryptedData( this, std::string( static_cast<const char*>( data ), len ) );
203 
204  return len;
205  }
206 
207  ssize_t GnuTLSBase::pushFunc( gnutls_transport_ptr_t ptr, const void* data, size_t len )
208  {
209  return static_cast<GnuTLSBase*>( ptr )->pushFunc( data, len );
210  }
211 
212  void GnuTLSBase::getCommonCertInfo()
213  {
214  const char* info;
215  info = gnutls_compression_get_name( gnutls_compression_get( *m_session ) );
216  if( info )
217  m_certInfo.compression = info;
218 
219  info = gnutls_mac_get_name( gnutls_mac_get( *m_session ) );
220  if( info )
221  m_certInfo.mac = info;
222 
223  info = gnutls_cipher_get_name( gnutls_cipher_get( *m_session ) );
224  if( info )
225  m_certInfo.cipher = info;
226 
227  switch( gnutls_protocol_get_version( *m_session ) )
228  {
229  // case GNUTLS_SSL3:
230  // m_certInfo.protocol = SSL3;
231  // break;
232  case GNUTLS_TLS1_0:
233  m_certInfo.protocol = "TLSv1";
234  break;
235  case GNUTLS_TLS1_1:
236  m_certInfo.protocol = "TLSv1.1";
237  break;
238  case GNUTLS_TLS1_2:
239  m_certInfo.protocol = "TLSv1.2";
240  break;
241  case GNUTLS_TLS1_3:
242  m_certInfo.protocol = "TLSv1.3";
243  break;
244  case GNUTLS_DTLS1_0:
245  m_certInfo.protocol = "DTLSv1";
246  break;
247  case GNUTLS_DTLS1_2:
248  m_certInfo.protocol = "DTLSv1.2";
249  break;
250  default:
251  m_certInfo.protocol = "Unknown protocol";
252  break;
253  }
254  }
255 
256 }
257 
258 #endif // HAVE_GNUTLS
virtual bool encrypt(const std::string &data)
virtual bool handshake()
virtual void cleanup()
virtual const std::string channelBinding() const
virtual bool hasChannelBinding() const
GnuTLSBase(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
const std::string EmptyString
Definition: gloox.cpp:124
std::string cipher
Definition: gloox.h:1002
std::string mac
Definition: gloox.h:1003
std::string protocol
Definition: gloox.h:1001
std::string compression
Definition: gloox.h:1004