IBR-DTNSuite  0.10
TLSStream.cpp
Go to the documentation of this file.
1 /*
2  * TLSStream.cpp
3  *
4  * Copyright (C) 2011 IBR, TU Braunschweig
5  *
6  * Written-by: Stephen Roettger <roettger@ibr.cs.tu-bs.de>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21 
23 
24 #include "ibrcommon/thread/Mutex.h"
26 
27 #include "ibrcommon/Logger.h"
28 
31 
32 #include "ibrcommon/data/File.h"
33 
34 #include <openssl/ssl.h>
35 #include <openssl/err.h>
36 
37 namespace ibrcommon
38 {
39  static const int ERR_BUF_SIZE = 256;
40  const std::string TLSStream::TAG = "TLSStream";
41 
42  SSL_CTX *TLSStream::_ssl_ctx = NULL;
43  bool TLSStream::_initialized = false;
44  bool TLSStream::_SSL_initialized = false;
45  ibrcommon::Mutex TLSStream::_initialization_lock;
46 
47  TLSStream::TLSStream(std::iostream *stream)
48  : iostream(this), _activated(false), in_buf_(BUFF_SIZE), out_buf_(BUFF_SIZE),
49  _stream(stream), _server(false), _ssl(NULL), _peer_cert(NULL), _iostreamBIO(NULL)
50  {
51  /* basic_streambuf related initialization */
52  // Initialize get pointer. This should be zero so that underflow is called upon first read.
53  setg(0, 0, 0);
54  setp(&out_buf_[0], &out_buf_[0] + BUFF_SIZE - 1);
55  }
56 
58  {
59  if(_ssl) SSL_free(_ssl);
60  if (_iostreamBIO != NULL) delete _iostreamBIO;
61  }
62 
63  void TLSStream::setServer(bool val)
64  {
65  _server = val;
66  }
67 
69  {
70  long error;
71 
72  ibrcommon::MutexLock l(_activation_lock);
73  if (_activated) {
74  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, warning) << "TLS has already been activated." << IBRCOMMON_LOGGER_ENDL;
75  return _peer_cert;
76  }
77 
78  /* check if TLSStream has already been initialized */
79  if (!_initialized) {
80  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, critical) << "TLSStream has not been initialized. Aborting activation." << IBRCOMMON_LOGGER_ENDL;
81  throw TLSNotInitializedException("TLSStream is not initialized.");
82  }
83 
84  /* create ssl object */
85  _ssl = SSL_new(_ssl_ctx);
86  if(!_ssl){
87  char err_buf[ERR_BUF_SIZE];
88  ERR_error_string_n(ERR_get_error(), err_buf, ERR_BUF_SIZE);
89  err_buf[ERR_BUF_SIZE - 1] = '\0';
90  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, critical) << "SSL Object creation failed: " << err_buf << IBRCOMMON_LOGGER_ENDL;
91  throw SSLCreationException(err_buf);
92  }
93 
94  /* set the ssl type (client or server) */
95  if(_server){
96  SSL_set_accept_state(_ssl);
97  } else {
98  SSL_set_connect_state(_ssl);
99  }
100 
101  /* create and assign BIO object */
102  try{
103  _iostreamBIO = new iostreamBIO(_stream);
104  //_bio = iostreamBIO::getBIO(_stream);
105  } catch(iostreamBIO::BIOException &ex){
106  throw BIOCreationException(ex.what());
107  }
108  try{
109  SSL_set_bio(_ssl, _iostreamBIO->getBIO(), _iostreamBIO->getBIO());
110  } catch(BIOCreationException &ex){
111  if (_iostreamBIO != NULL) delete _iostreamBIO;
112  _iostreamBIO = NULL;
113  SSL_free(_ssl);
114  _ssl = NULL;
115  throw;
116  }
117 
118  /* perform TLS Handshake */
119  error = SSL_do_handshake(_ssl);
120  if(error <= 0){ /* on 0 the handshake failed but was shut down controlled */
121  int errcode = SSL_get_error(_ssl, static_cast<int>(error));
122 
123  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, error) << "TLS handshake failed: " << log_error_msg(errcode) << IBRCOMMON_LOGGER_ENDL;
124 
125  /* cleanup */
126  if (_iostreamBIO != NULL) delete _iostreamBIO;
127  _iostreamBIO = NULL;
128  SSL_free(_ssl);
129  _ssl = NULL;
130  throw TLSHandshakeException(log_error_msg(errcode));
131  }
132 
133  _peer_cert = SSL_get_peer_certificate(_ssl);
134  if((error = SSL_get_verify_result(_ssl)) != X509_V_OK || _peer_cert == NULL){
135  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, error) << "Peer certificate could not be verified. Error: " << error << ". Error codes are documented in \"man 1 verify\"." << IBRCOMMON_LOGGER_ENDL;
136  if(_peer_cert){
137  X509_free(_peer_cert);
138  _peer_cert = NULL;
139  }
140  if (_iostreamBIO != NULL) delete _iostreamBIO;
141  _iostreamBIO = NULL;
142  SSL_free(_ssl);
143  _ssl = NULL;
144  std::stringstream ss; ss << "Certificate verification error " << error << ".";
145  throw TLSCertificateVerificationException(ss.str());
146  }
147 
148  _activated = true;
149 
150  return _peer_cert;
151  }
152 
153  TLSStream::traits::int_type TLSStream::underflow()
154  {
155  int num_bytes = 0;
156  /* get data from tcpstream */
157 
158  if(_activated)
159  {
160  /* decrypt */
161  num_bytes = SSL_read(_ssl, &in_buf_[0], BUFF_SIZE);
162  if(num_bytes == 0){
163  /* connection closed */
164  if(SSL_get_error(_ssl, num_bytes) == SSL_ERROR_ZERO_RETURN){
165  /* clean shutdown */
166  close();
167  } else {
168  IBRCOMMON_LOGGER_DEBUG_TAG(TLSStream::TAG, 60) << "underflow() " << log_error_msg(SSL_get_error(_ssl, num_bytes)) << IBRCOMMON_LOGGER_ENDL;
169  }
170  return traits::eof();
171  } else if(num_bytes < 0) {
172  IBRCOMMON_LOGGER_DEBUG_TAG(TLSStream::TAG, 60) << "underflow() " << log_error_msg(SSL_get_error(_ssl, num_bytes)) << IBRCOMMON_LOGGER_ENDL;
173  return traits::eof();
174  }
175  }
176  else
177  {
178  try{
179  /* make sure to read at least 1 byte and then read as much as we can */
180  num_bytes = static_cast<int>( _stream->read(&in_buf_[0], 1).readsome(&in_buf_[0]+1, BUFF_SIZE-1) + 1 );
181  } catch(ios_base::failure &ex){
182  /* ignore, the specific bits are checked later */
183  }
184  /* error checking, these can probably not happen because the ConnectionClosedException is thrown first */
185  if(_stream->eof()){
186  return traits::eof();
187  }
188  if(_stream->fail()){
189  IBRCOMMON_LOGGER_DEBUG_TAG(TLSStream::TAG, 60) << "underlying stream failed while reading." << IBRCOMMON_LOGGER_ENDL;
190  return traits::eof();
191  }
192  if(_stream->bad()){
193  IBRCOMMON_LOGGER_DEBUG_TAG(TLSStream::TAG, 60) << "underlying stream went bad while reading." << IBRCOMMON_LOGGER_ENDL;
194  return traits::eof();
195  }
196  }
197 
198  setg(&in_buf_[0], &in_buf_[0], &in_buf_[0] + num_bytes);
199 
200  return traits::not_eof(in_buf_[0]);
201  }
202 
203  TLSStream::traits::int_type TLSStream::overflow(traits::int_type c)
204  {
205  int num_bytes;
206  char *ibegin = &out_buf_[0];
207  char *iend = pptr();
208 
209  // mark the buffer as free
210  setp(&out_buf_[0], &out_buf_[0] + BUFF_SIZE - 1);
211 
212  // append the last character
213  if(!traits::eq_int_type(c, traits::eof())) {
214  *iend++ = traits::to_char_type(c);
215  }
216 
217  // if there is nothing to send, just return
218  if ((iend - ibegin) == 0)
219  {
220  return traits::not_eof(c);
221  }
222 
223  if(_activated)
224  {
225  /* use ssl to send */
226  num_bytes = SSL_write(_ssl, &out_buf_[0], static_cast<int>(iend - ibegin));
227  if(num_bytes == 0){
228  /* connection closed */
229  return traits::eof();
230  } else if(num_bytes < 0){
231  IBRCOMMON_LOGGER_DEBUG_TAG(TLSStream::TAG, 60) << "overflow() " << log_error_msg(SSL_get_error(_ssl, num_bytes)) << IBRCOMMON_LOGGER_ENDL;
232  return traits::eof();
233  }
234  }
235  else
236  {
237  try{
238  _stream->write(&out_buf_[0], (iend - ibegin));
239  } catch(ios_base::failure &ex){
240  /* ignore, the badbit is checked instead */
241  }
242  if (_stream->bad()) {
243  IBRCOMMON_LOGGER_DEBUG_TAG(TLSStream::TAG, 60) << "underlying stream went bad while writing." << IBRCOMMON_LOGGER_ENDL;
244  return traits::eof();
245  }
246  }
247 
248  return traits::not_eof(c);
249  }
250 
251  void TLSStream::init(X509 *certificate, EVP_PKEY *privateKey, ibrcommon::File trustedCAPath, bool enableEncryption)
252  {
253  ibrcommon::MutexLock l(_initialization_lock);
254  if(_initialized){
255  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, info) << "TLS has already been initialized." << IBRCOMMON_LOGGER_ENDL;
256  return;
257  }
258 
259  /* openssl initialization */
260  /* the if block is needed because SSL_library_init() is not reentrant */
261  if(!_SSL_initialized){
262  SSL_load_error_strings();
263  SSL_library_init();
264  ERR_load_BIO_strings();
265  ERR_load_SSL_strings();
266  _SSL_initialized = true;
267  }
268 
269 
270  /* create ssl context and throw exception if it fails */
271  _ssl_ctx = SSL_CTX_new(TLSv1_method());
272  if(!_ssl_ctx){
273  char err_buf[ERR_BUF_SIZE];
274  ERR_error_string_n(ERR_get_error(), err_buf, ERR_BUF_SIZE);
275  err_buf[ERR_BUF_SIZE - 1] = '\0';
276  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, critical) << "TLS/SSL Context Object creation failed: " << err_buf << IBRCOMMON_LOGGER_ENDL;
277  throw ContextCreationException(err_buf);
278  }
279 
280  /* set verification mode */
281  /* client and server require a valid certificate or the handshake fails */
282  SSL_CTX_set_verify(_ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
283 
284  /* load the trusted certificates from CAPath */
285  if(trustedCAPath.isDirectory()){
286  if(SSL_CTX_load_verify_locations(_ssl_ctx, NULL, trustedCAPath.getPath().c_str()) != 1){
287  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, error) << "SSL_CTX_load_verify_locations failed." << IBRCOMMON_LOGGER_ENDL;
288  }
289  } else {
290  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, info) << "TLS initialization without trusted certificates." << IBRCOMMON_LOGGER_ENDL;
291  }
292 
293  /* load the certificate and private key
294  * the certificate chain will be completed by openssl with certificates from CAPath */
295  if(!certificate || SSL_CTX_use_certificate(_ssl_ctx, certificate) != 1){
296  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, critical) << "Could not load the certificate." << IBRCOMMON_LOGGER_ENDL;
297  SSL_CTX_free(_ssl_ctx);
298  _ssl_ctx = NULL;
299  throw TLSCertificateFileException("Could not load the certificate.");
300  }
301  if(!privateKey || SSL_CTX_use_PrivateKey(_ssl_ctx, privateKey) != 1){
302  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, critical) << "Could not load the private key." << IBRCOMMON_LOGGER_ENDL;
303  SSL_CTX_free(_ssl_ctx);
304  _ssl_ctx = NULL;
305  throw TLSKeyFileException("Could not load the private key.");
306  }
307 
308  if(trustedCAPath.isDirectory()){
309  std::list<ibrcommon::File> files;
310  if(trustedCAPath.getFiles(files) == 0){
311  /* success */
312  for(std::list<ibrcommon::File>::iterator it = files.begin();
313  it != files.end(); ++it){
314  if(it->getType() == DT_LNK || it->isSystem() || it->isDirectory())
315  continue;
316 
317  X509 *cert = NULL;
318  FILE *fp = fopen(it->getPath().c_str(), "r");
319  if(fp && PEM_read_X509(fp, &cert, NULL, NULL)){
320  if(SSL_CTX_add_client_CA(_ssl_ctx, cert) != 1){
321  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, error) << "could not add client CA from file: " << it->getPath() << "." << IBRCOMMON_LOGGER_ENDL;
322  }
323  } else {
324  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, error) << "could not read certificate from " << it->getPath() << "." << IBRCOMMON_LOGGER_ENDL;
325  }
326  }
327  } else {
328  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, error) << "Could not get files from CADirectory." << IBRCOMMON_LOGGER_ENDL;
329  }
330  }
331 
332  if(!enableEncryption){
333  if(!SSL_CTX_set_cipher_list(_ssl_ctx, "eNULL")){
334  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, critical) << "Could not set the cipherlist." << IBRCOMMON_LOGGER_ENDL;
335  }
336  }
337 
338  _initialized = true;
339  IBRCOMMON_LOGGER_TAG(TLSStream::TAG, info) << "Initialization succeeded." << IBRCOMMON_LOGGER_ENDL;
340  }
341 
343  {
344  ibrcommon::MutexLock l(_initialization_lock);
345  if(!_initialized)
346  return;
347 
348  /* remove the SSL Context */
349  if(_ssl_ctx){
350  SSL_CTX_free(_ssl_ctx);
351  _ssl_ctx = NULL;
352  }
353 
354  _initialized = false;
355  }
356 
358  return _initialized;
359  }
360 
362  if(_ssl && _stream->good()){
363  int ret;
364  if((ret = SSL_shutdown(_ssl)) == -1) {
365  IBRCOMMON_LOGGER_DEBUG_TAG(TLSStream::TAG, 60) << "SSL_shutdown error " << log_error_msg(SSL_get_error(_ssl, ret)) << IBRCOMMON_LOGGER_ENDL;
366  } else if(ret == 0){
367  /* try again */
368  if((ret = SSL_shutdown(_ssl)) == -1){
369  /* this can happen for example if the peer does not send a shutdown message */
370  IBRCOMMON_LOGGER_DEBUG_TAG(TLSStream::TAG, 60) << "SSL_shutdown error " << log_error_msg(SSL_get_error(_ssl, ret)) << IBRCOMMON_LOGGER_ENDL;
371  }
372  }
373  }
374  IBRCOMMON_LOGGER_DEBUG_TAG(TLSStream::TAG, 60) << "Connection closed." << IBRCOMMON_LOGGER_ENDL;
375  }
376 
378  {
379  int ret = traits::eq_int_type(this->overflow(
380  traits::eof()), traits::eof()) ? -1
381  : 0;
382 
383  /* flush underlying stream */
384  _stream->flush();
385 
386  return ret;
387  }
388 
389  std::string TLSStream::log_error_msg(int errnumber)
390  {
391  std::string ret;
392 
393  switch(errnumber){
394  case SSL_ERROR_NONE:
395  ret = "No Error.";
396  break;
397  case SSL_ERROR_ZERO_RETURN:
398  ret = "TLS Connection has been closed.";
399  break;
400  case SSL_ERROR_WANT_READ:
401  case SSL_ERROR_WANT_WRITE:
402  ret = "WANT_READ/WANT_WRITE";
403  break;
404  case SSL_ERROR_WANT_CONNECT:
405  case SSL_ERROR_WANT_ACCEPT:
406  ret = "WANT_CONNECT/WANT_ACCEPT";
407  break;
408  case SSL_ERROR_WANT_X509_LOOKUP:
409  ret = "WANT_X509_LOOKUP";
410  break;
411  case SSL_ERROR_SYSCALL:
412  ret = "Syscall error.";
413  break;
414  case SSL_ERROR_SSL:{
415  char err_buf[ERR_BUF_SIZE];
416  ERR_error_string_n(ERR_get_error(), err_buf, ERR_BUF_SIZE);
417  err_buf[ERR_BUF_SIZE - 1] = '\0';
418  std::stringstream ss; ss << "SSL error: \"" << err_buf << "\".";
419  ret = ss.str();
420  break;
421  }
422  default:{
423  std::stringstream ss; ss << "Unknown error code " << errnumber << ".";
424  ret = ss.str();
425  break;
426  }
427  }
428 
429  return ret;
430  }
431 
432 }