gloox  1.0.9
tlsgnutlsbase.cpp
1 /*
2  Copyright (c) 2005-2013 by Jakob Schroeter <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 #include <cstdlib>
25 #include <cstring>
26 
27 namespace gloox
28 {
29 
30  GnuTLSBase::GnuTLSBase( TLSHandler* th, const std::string& server )
31  : TLSBase( th, server ), m_session( new gnutls_session_t ), m_buf( 0 ), m_bufsize( 17000 )
32  {
33  m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) );
34  }
35 
37  {
38  free( m_buf );
39  m_buf = 0;
40  cleanup();
41  delete m_session;
42 // FIXME: It segfaults if more then one account uses
43 // encryption at same time, so we comment it for now.
44 // Do we need to deinit at all?
45 // gnutls_global_deinit();
46  }
47 
48  bool GnuTLSBase::encrypt( const std::string& data )
49  {
50  if( !m_secure )
51  {
52  handshake();
53  return true;
54  }
55 
56  ssize_t ret = 0;
57  std::string::size_type sum = 0;
58  do
59  {
60  ret = gnutls_record_send( *m_session, data.c_str() + sum, data.length() - sum );
61  sum += ret;
62  }
63  while( ( ret == GNUTLS_E_AGAIN ) || ( ret == GNUTLS_E_INTERRUPTED ) || sum < data.length() );
64  return true;
65  }
66 
67  int GnuTLSBase::decrypt( const std::string& data )
68  {
69  m_recvBuffer += data;
70 
71  if( !m_secure )
72  {
73  handshake();
74  return static_cast<int>( data.length() );
75  }
76 
77  int sum = 0;
78  int ret = 0;
79  do
80  {
81  ret = static_cast<int>( gnutls_record_recv( *m_session, m_buf, m_bufsize ) );
82 
83  if( ret > 0 && m_handler )
84  {
85  m_handler->handleDecryptedData( this, std::string( m_buf, ret ) );
86  sum += ret;
87  }
88  }
89  while( ret > 0 );
90 
91  return sum;
92  }
93 
95  {
96  if( !m_mutex.trylock() )
97  return;
98 
99  TLSHandler* handler = m_handler;
100  m_handler = 0;
101  gnutls_bye( *m_session, GNUTLS_SHUT_RDWR );
102  gnutls_db_remove_session( *m_session );
103  gnutls_credentials_clear( *m_session );
104  if( m_session )
105  gnutls_deinit( *m_session );
106 
107  delete m_session;
108 
109  m_secure = false;
110  m_valid = false;
111  m_session = 0;
112  m_session = new gnutls_session_t;
113  m_handler = handler;
114 
115  m_mutex.unlock();
116  }
117 
119  {
120  if( !m_handler )
121  return false;
122 
123  int ret = gnutls_handshake( *m_session );
124  if( ret < 0 && gnutls_error_is_fatal( ret ) )
125  {
126  gnutls_perror( ret );
127  gnutls_db_remove_session( *m_session );
128  gnutls_deinit( *m_session );
129  m_valid = false;
130 
131  m_handler->handleHandshakeResult( this, false, m_certInfo );
132  return false;
133  }
134  else if( ret == GNUTLS_E_AGAIN )
135  {
136  return true;
137  }
138 
139  m_secure = true;
140 
141  getCertInfo();
142 
143  m_handler->handleHandshakeResult( this, true, m_certInfo );
144  return true;
145  }
146 
148  {
149 #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
150  return true;
151 #else
152  return false;
153 #endif
154  }
155 
156  const std::string GnuTLSBase::channelBinding() const
157  {
158 #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
159  gnutls_datum_t cb;
160  int rc;
161  rc = gnutls_session_channel_binding( *m_session, GNUTLS_CB_TLS_UNIQUE, &cb );
162  if( !rc )
163  return std::string( (char*)cb.data, cb.size );
164  else
165 #endif
166  return EmptyString;
167  }
168 
169  ssize_t GnuTLSBase::pullFunc( void* data, size_t len )
170  {
171  ssize_t cpy = ( len > m_recvBuffer.length() ) ? ( m_recvBuffer.length() ) : ( len );
172  if( cpy > 0 )
173  {
174  memcpy( data, (const void*)m_recvBuffer.c_str(), cpy );
175  m_recvBuffer.erase( 0, cpy );
176  return cpy;
177  }
178  else
179  {
180  errno = EAGAIN;
181  return GNUTLS_E_AGAIN;
182  }
183  }
184 
185  ssize_t GnuTLSBase::pullFunc( gnutls_transport_ptr_t ptr, void* data, size_t len )
186  {
187  return static_cast<GnuTLSBase*>( ptr )->pullFunc( data, len );
188  }
189 
190  ssize_t GnuTLSBase::pushFunc( const void* data, size_t len )
191  {
192  if( m_handler )
193  m_handler->handleEncryptedData( this, std::string( (const char*)data, len ) );
194 
195  return len;
196  }
197 
198  ssize_t GnuTLSBase::pushFunc( gnutls_transport_ptr_t ptr, const void* data, size_t len )
199  {
200  return static_cast<GnuTLSBase*>( ptr )->pushFunc( data, len );
201  }
202 
203 }
204 
205 #endif // HAVE_GNUTLS
virtual const std::string channelBinding() const
GnuTLSBase(TLSHandler *th, const std::string &server=EmptyString)
virtual void handleEncryptedData(const TLSBase *base, const std::string &data)=0
virtual int decrypt(const std::string &data)
virtual void cleanup()
virtual bool handshake()
virtual bool hasChannelBinding() const
An abstract base class for TLS implementations.
Definition: tlsbase.h:31
virtual void handleDecryptedData(const TLSBase *base, const std::string &data)=0
virtual bool encrypt(const std::string &data)
An interface that allows for interacting with TLS implementations derived from TLSBase.
Definition: tlshandler.h:34
const std::string EmptyString
Definition: gloox.cpp:122
virtual void handleHandshakeResult(const TLSBase *base, bool success, CertInfo &certinfo)=0