IBR-DTNSuite 0.6

ibrcommon/ibrcommon/net/TLSStream.cpp

Go to the documentation of this file.
00001 /*
00002  * TLSStream.cpp
00003  *
00004  *  Created on: Mar 24, 2011
00005  *      Author: roettger
00006  */
00007 
00008 #include "TLSStream.h"
00009 
00010 #include "ibrcommon/thread/Mutex.h"
00011 #include "ibrcommon/thread/MutexLock.h"
00012 
00013 #include "ibrcommon/Logger.h"
00014 
00015 #include "ibrcommon/net/iostreamBIO.h"
00016 #include "ibrcommon/TLSExceptions.h"
00017 
00018 #include "ibrcommon/data/File.h"
00019 
00020 #include <openssl/ssl.h>
00021 #include <openssl/err.h>
00022 
00023 namespace ibrcommon
00024 {
00025         static const int ERR_BUF_SIZE = 256;
00026 
00027         SSL_CTX *TLSStream::_ssl_ctx = NULL;
00028         bool TLSStream::_initialized = false;
00029         bool TLSStream::_SSL_initialized = false;
00030         ibrcommon::Mutex TLSStream::_initialization_lock;
00031 
00032         TLSStream::TLSStream(std::iostream *stream) :
00033                 _stream(stream), in_buf_(new char[BUFF_SIZE]), out_buf_(new char[BUFF_SIZE]),
00034                 _server(false), _activated(false), iostream(this), _ssl(NULL), _iostreamBIO(NULL)
00035     {
00036                 /* basic_streambuf related initialization */
00037                 // Initialize get pointer.  This should be zero so that underflow is called upon first read.
00038                 setg(0, 0, 0);
00039                 setp(out_buf_, out_buf_ + BUFF_SIZE - 1);
00040     }
00041 
00042     TLSStream::~TLSStream()
00043         {
00044         if(_ssl) SSL_free(_ssl);
00045                 delete[] in_buf_;
00046                 delete[] out_buf_;
00047         }
00048 
00049     void TLSStream::setServer(bool val)
00050     {
00051         _server = val;
00052     }
00053 
00054     X509 *TLSStream::activate()
00055     {
00056         int error;
00057 
00058         ibrcommon::MutexLock l(_activation_lock);
00059         if(_activated){
00060                 IBRCOMMON_LOGGER(info) << "TLS has already been activated." << IBRCOMMON_LOGGER_ENDL;
00061                 return _peer_cert;
00062         }
00063 
00064         /* check if TLSStream has already been initialized */
00065                 if(!_initialized){
00066                         IBRCOMMON_LOGGER(critical) << "TLSStream: TLSStream has not been initialized. Aborting activation." << IBRCOMMON_LOGGER_ENDL;
00067                         throw TLSNotInitializedException("TLSStream is not initialized.");
00068                 }
00069 
00070                 /* create ssl object */
00071                 _ssl = SSL_new(_ssl_ctx);
00072                 if(!_ssl){
00073                         char err_buf[ERR_BUF_SIZE];
00074                         ERR_error_string_n(ERR_get_error(), err_buf, ERR_BUF_SIZE);
00075                         err_buf[ERR_BUF_SIZE - 1] = '\0';
00076                         IBRCOMMON_LOGGER(critical) << "TLSStream: SSL Object creation failed: " << err_buf << IBRCOMMON_LOGGER_ENDL;
00077                         throw SSLCreationException(err_buf);
00078                 }
00079 
00080                 /* set the ssl type (client or server) */
00081                 if(_server){
00082                         SSL_set_accept_state(_ssl);
00083                 } else {
00084                         SSL_set_connect_state(_ssl);
00085                 }
00086 
00087                 /* create and assign BIO object */
00088                 try{
00089                         _iostreamBIO = std::auto_ptr<iostreamBIO>(new iostreamBIO(_stream));
00090                         //_bio = iostreamBIO::getBIO(_stream);
00091                 } catch(iostreamBIO::BIOException &ex){
00092                         throw BIOCreationException(ex.what());
00093                 }
00094                 try{
00095                         SSL_set_bio(_ssl, _iostreamBIO->getBIO(), _iostreamBIO->getBIO());
00096                 } catch(BIOCreationException &ex){
00097                         _iostreamBIO = std::auto_ptr<iostreamBIO>(NULL);
00098                         SSL_free(_ssl);
00099                         _ssl = NULL;
00100                         throw;
00101                 }
00102 
00103         /* perform TLS Handshake */
00104 //              if(_server){
00105 //                      error = SSL_accept(_ssl);
00106 //              } else {
00107 //                      error = SSL_connect(_ssl);
00108 //              }
00109                 error = SSL_do_handshake(_ssl);
00110                 if(error <= 0){ /* on 0 the handshake failed but was shut down controlled */
00111                         std::string err_msg = switchTLSError(SSL_get_error(_ssl, error));
00112                         IBRCOMMON_LOGGER(critical) << "TLSStream: TLS Handshake failed: " << err_msg << IBRCOMMON_LOGGER_ENDL;
00113 
00114                         /* cleanup */
00115                         _iostreamBIO = std::auto_ptr<iostreamBIO>(NULL);
00116                         SSL_free(_ssl);
00117                         _ssl = NULL;
00118                         throw TLSHandshakeException(err_msg);
00119                 }
00120 
00121                 _peer_cert = SSL_get_peer_certificate(_ssl);
00122                 if((error = SSL_get_verify_result(_ssl)) != X509_V_OK || _peer_cert == NULL){
00123                         IBRCOMMON_LOGGER(error) << "TLSStream: Peer Certificate could not be verified. Error: " << error << ". Error Codes are documented in \"man 1 verify\"." << IBRCOMMON_LOGGER_ENDL;
00124                         if(_peer_cert){
00125                                 X509_free(_peer_cert);
00126                                 _peer_cert = NULL;
00127                         }
00128                         _iostreamBIO = std::auto_ptr<iostreamBIO>(NULL);
00129                         SSL_free(_ssl);
00130                         _ssl = NULL;
00131                         std::stringstream ss; ss << "TLSStream: Certificate verification error " << error << ".";
00132                         throw TLSCertificateVerificationException(ss.str());
00133                 }
00134 
00135         _activated = true;
00136 
00137         return _peer_cert;
00138     }
00139 
00140     int TLSStream::underflow()
00141     {
00142         streamsize num_bytes;
00143         /* get data from tcpstream */
00144 
00145         if(_activated){
00146                 /* decrypt */
00147 //              try{
00148                         num_bytes = SSL_read(_ssl, in_buf_, BUFF_SIZE);
00149                         if(num_bytes == 0){
00150                                 /* connection closed */
00151                                 if(SSL_get_error(_ssl, num_bytes) == SSL_ERROR_ZERO_RETURN){
00152                                         /* clean shutdown */
00153                                         close();
00154                                 } else {
00155                                         IBRCOMMON_LOGGER(error) << switchTLSError(SSL_get_error(_ssl, num_bytes)) << IBRCOMMON_LOGGER_ENDL;
00156                                 }
00157                                 return traits::eof();
00158                         } else if(num_bytes < 0){
00159                                 IBRCOMMON_LOGGER(error) << switchTLSError(SSL_get_error(_ssl, num_bytes)) << IBRCOMMON_LOGGER_ENDL;
00160                                 return traits::eof();
00161                         }
00162 //              }
00163 //              catch(...){ //exceptions are to be caught at a higher layer
00164 //                      return traits::eof();
00165 //              }
00166         } else {
00167                 try{
00168                         /* make sure to read at least 1 byte and then read as much as we can */
00169                         num_bytes = _stream->read(in_buf_, 1).readsome(in_buf_+1, BUFF_SIZE-1) + 1;
00170                 } catch(ios_base::failure &ex){
00171                         /* ignore, the specific bits are checked later */
00172                 }
00173 //              catch(ConnectionClosedException &ex){
00174 //                      /* throw aswell or return eof? */
00175 //                      throw;
00176 //              }
00177                 /* error checking, these can probably not happen because the ConnectionClosedException is thrown first */
00178                 if(_stream->eof()){
00179                         return traits::eof();
00180                 }
00181                 if(_stream->fail()){
00182                         IBRCOMMON_LOGGER(error) << "TLSStream: underlying stream failed while reading." << IBRCOMMON_LOGGER_ENDL;
00183                         return traits::eof();
00184                 }
00185                 if(_stream->bad()){
00186                         IBRCOMMON_LOGGER(error) << "TLSStream: underlying stream went bad while reading." << IBRCOMMON_LOGGER_ENDL;
00187                         return traits::eof();
00188                 }
00189         }
00190 
00191         setg(in_buf_, in_buf_, in_buf_ + num_bytes);
00192 
00193         return traits::not_eof(in_buf_[0]);
00194     }
00195 
00196     int TLSStream::overflow(int c)
00197     {
00198         streamsize num_bytes;
00199                 char *ibegin = out_buf_;
00200                 char *iend = pptr();
00201 
00202         // mark the buffer as free
00203                 setp(out_buf_, out_buf_ + BUFF_SIZE - 1);
00204 
00205                 // append the last character
00206                 if(!traits::eq_int_type(c, traits::eof())) {
00207                         *iend++ = traits::to_char_type(c);
00208                 }
00209 
00210                 // if there is nothing to send, just return
00211                 if ((iend - ibegin) == 0)
00212                 {
00213                         return traits::not_eof(c);
00214                 }
00215 
00216         if(_activated){
00217                 /* use ssl to send */
00218 //              try{
00219                                 num_bytes = SSL_write(_ssl, out_buf_, (iend - ibegin));
00220                                 if(num_bytes == 0){
00221                                         /* connection closed */
00222                                         return traits::eof();
00223                                 } else if(num_bytes < 0){
00224                                         IBRCOMMON_LOGGER(error) << switchTLSError(SSL_get_error(_ssl, num_bytes)) << IBRCOMMON_LOGGER_ENDL;
00225                                         return traits::eof();
00226                                 }
00227 //              } catch(...){
00228 //                      return traits::eof();
00229 //              }
00230         } else {
00231                 try{
00232                         _stream->write(out_buf_, (iend - ibegin));
00233                 } catch(ios_base::failure &ex){
00234                         /* ignore, the badbit is checked instead */
00235                 }
00236 //              catch(ConnectionClosedException &ex){
00237 //                      /* throw or return eof? */
00238 //                      throw;
00239 //              }
00240                 if(_stream->bad()){
00241                         IBRCOMMON_LOGGER(error) << "TLSStream: underlying stream went bad while writing." << IBRCOMMON_LOGGER_ENDL;
00242                         return traits::eof();
00243                 }
00244         }
00245 
00246         return traits::not_eof(c);
00247     }
00248 
00249     void TLSStream::init(X509 *certificate, EVP_PKEY *privateKey, ibrcommon::File trustedCAPath, bool enableEncryption)
00250     {
00251         ibrcommon::MutexLock l(_initialization_lock);
00252         if(_initialized){
00253                 IBRCOMMON_LOGGER(info) << "TLSStream: TLS has already been initialized." << IBRCOMMON_LOGGER_ENDL;
00254                 return;
00255         }
00256 
00257         /* openssl initialization */
00258         /* the if block is needed because SSL_library_init() is not reentrant */
00259         if(!_SSL_initialized){
00260                         SSL_load_error_strings();
00261                         SSL_library_init();
00262                         ERR_load_BIO_strings();
00263                         ERR_load_SSL_strings();
00264                         _SSL_initialized = true;
00265         }
00266 
00267 
00268         /* create ssl context and throw exception if it fails */
00269         _ssl_ctx = SSL_CTX_new(TLSv1_method());
00270         if(!_ssl_ctx){
00271                 char err_buf[ERR_BUF_SIZE];
00272             ERR_error_string_n(ERR_get_error(), err_buf, ERR_BUF_SIZE);
00273             err_buf[ERR_BUF_SIZE - 1] = '\0';
00274             IBRCOMMON_LOGGER(critical) << "TLSStream: TLS/SSL Context Object creation failed: " << err_buf << IBRCOMMON_LOGGER_ENDL;
00275                 throw ContextCreationException(err_buf);
00276         }
00277 
00278         /* set verification mode */
00279         /* client and server require a valid certificate or the handshake fails */
00280         SSL_CTX_set_verify(_ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
00281 
00282         /* load the trusted certificates from CAPath */
00283         if(trustedCAPath.isDirectory()){
00284                         if(SSL_CTX_load_verify_locations(_ssl_ctx, NULL, trustedCAPath.getPath().c_str()) != 1){
00285                            IBRCOMMON_LOGGER(error) << "TLSStream: SSL_CTX_load_verify_locations failed." << IBRCOMMON_LOGGER_ENDL;
00286                         }
00287         } else {
00288                 IBRCOMMON_LOGGER(info) << "TLSStream: TLS initialization without trusted certificates." << IBRCOMMON_LOGGER_ENDL;
00289         }
00290 
00291         /* load the certificate and private key
00292          * the certificate chain will be completed by openssl with certificates from CAPath */
00293         if(!certificate || SSL_CTX_use_certificate(_ssl_ctx, certificate) != 1){
00294                 IBRCOMMON_LOGGER(critical) << "TLSStream: Could not load the certificate." << IBRCOMMON_LOGGER_ENDL;
00295                 SSL_CTX_free(_ssl_ctx);
00296                 _ssl_ctx = NULL;
00297                 throw TLSCertificateFileException("Could not load the certificate.");
00298         }
00299         if(!privateKey || SSL_CTX_use_PrivateKey(_ssl_ctx, privateKey) != 1){
00300                 IBRCOMMON_LOGGER(critical) << "TLSStream: Could not load the private key." << IBRCOMMON_LOGGER_ENDL;
00301                 SSL_CTX_free(_ssl_ctx);
00302                 _ssl_ctx = NULL;
00303                 throw TLSKeyFileException("Could not load the private key.");
00304         }
00305 
00306         if(trustedCAPath.isDirectory()){
00307                 std::list<ibrcommon::File> files;
00308                 if(trustedCAPath.getFiles(files) == 0){
00309                         /* success */
00310                         for(std::list<ibrcommon::File>::iterator it = files.begin();
00311                                         it != files.end(); ++it){
00312                                 if(it->getType() == DT_LNK || it->isSystem() || it->isDirectory())
00313                                         continue;
00314 
00315                                 X509 *cert = NULL;
00316                                 FILE *fp = fopen(it->getPath().c_str(), "r");
00317                                 if(fp && PEM_read_X509(fp, &cert, NULL, NULL)){
00318                                         if(SSL_CTX_add_client_CA(_ssl_ctx, cert) != 1){
00319                                                 IBRCOMMON_LOGGER(error) << "TLSStream: could not add client CA from file: " << it->getPath() << "." << IBRCOMMON_LOGGER_ENDL;
00320                                         }
00321                                 } else {
00322                                         IBRCOMMON_LOGGER(error) << "TLSStream: could not read certificate from " << it->getPath() << "." << IBRCOMMON_LOGGER_ENDL;
00323                                 }
00324                         }
00325                 } else {
00326                         IBRCOMMON_LOGGER(error) << "TLSStream: Could not get files from CADirectory." << IBRCOMMON_LOGGER_ENDL;
00327                 }
00328         }
00329 
00330         if(!enableEncryption){
00331                         if(!SSL_CTX_set_cipher_list(_ssl_ctx, "eNULL")){
00332                                 IBRCOMMON_LOGGER(critical) << "TLSStream: Could not set the cipherlist." << IBRCOMMON_LOGGER_ENDL;
00333                         }
00334         }
00335 
00336         _initialized = true;
00337         IBRCOMMON_LOGGER(info) << "TLSStream: Initialization succeeded." << IBRCOMMON_LOGGER_ENDL;
00338     }
00339 
00340     void TLSStream::flushInitialization()
00341     {
00342         ibrcommon::MutexLock l(_initialization_lock);
00343         if(!_initialized)
00344                 return;
00345 
00346         /* remove the SSL Context */
00347         if(_ssl_ctx){
00348                 SSL_CTX_free(_ssl_ctx);
00349                 _ssl_ctx = NULL;
00350         }
00351 
00352         _initialized = false;
00353     }
00354 
00355     bool TLSStream::isInitialized(){
00356         return _initialized;
00357     }
00358 
00359     void TLSStream::close(){
00360         if(_ssl && _stream->good()){
00361                 int ret;
00362                 if((ret = SSL_shutdown(_ssl)) == -1){
00363                         IBRCOMMON_LOGGER_DEBUG(15) << "TLSStream: SSL_shutdown error." << switchTLSError(SSL_get_error(_ssl, ret)) << IBRCOMMON_LOGGER_ENDL;
00364                 } else if(ret == 0){
00365                         /* try again */
00366                         if((ret = SSL_shutdown(_ssl)) == -1){
00367                                 /* this can happen for example if the peer does not send a shutdown message */
00368                             IBRCOMMON_LOGGER_DEBUG(15) << "TLSStream: SSL_shutdown error (2): " << switchTLSError(SSL_get_error(_ssl, ret)) << " The reason can be, that the peer did not send a Shutdown message." << IBRCOMMON_LOGGER_ENDL;
00369                         }
00370                 }
00371         }
00372         IBRCOMMON_LOGGER_DEBUG(25) << "TLS Connection closed." << IBRCOMMON_LOGGER_ENDL;
00373     }
00374 
00375     int TLSStream::sync()
00376     {
00377                 int ret = traits::eq_int_type(this->overflow(
00378                                 traits::eof()), traits::eof()) ? -1
00379                                 : 0;
00380 
00381                 /* flush underlying stream */
00382 //              try{
00383                         _stream->flush();
00384 //              }
00385 //              catch(ConnectionClosedException &ex){
00386 //                      throw;
00387 //                      //ret = traits::eof();
00388 //              }
00389 //              catch(ios_base::failure &ex){
00390 //                      throw;
00391 //              }
00392 
00393                 return ret;
00394     }
00395 
00396     std::string
00397     TLSStream::switchTLSError(int errnumber){
00398         std::string ret;
00399 
00400         switch(errnumber){
00401         case SSL_ERROR_NONE:
00402                 ret = "TLSStream: No Error.";
00403                 break;
00404         case SSL_ERROR_ZERO_RETURN:
00405                 ret = "TLSStream: SSL Error: TLS Connection has been closed.";
00406                 break;
00407         case SSL_ERROR_WANT_READ:
00408         case SSL_ERROR_WANT_WRITE:
00409                 ret = "TLSStream: SSL Error: WANT_READ/WANT_WRITE";
00410                 break;
00411         case SSL_ERROR_WANT_CONNECT:
00412         case SSL_ERROR_WANT_ACCEPT:
00413                 ret = "TLSStream: SSL Error: WANT_CONNECT/WANT_ACCEPT";
00414                 break;
00415         case SSL_ERROR_WANT_X509_LOOKUP:
00416                 ret = "TLSStream: SSL Error: WANT_X509_LOOKUP";
00417                 break;
00418         case SSL_ERROR_SYSCALL:
00419                 ret = "TLSStream: SSL Error: Syscall error.";
00420                 break;
00421         case SSL_ERROR_SSL:{
00422                 char err_buf[ERR_BUF_SIZE];
00423                 ERR_error_string_n(ERR_get_error(), err_buf, ERR_BUF_SIZE);
00424                 err_buf[ERR_BUF_SIZE - 1] = '\0';
00425                 std::stringstream ss; ss << "TLSStream: SSL Error: Protocol Error: \"" << err_buf << "\".";
00426                 ret = ss.str();
00427                 break;
00428         }
00429         default:{
00430                 std::stringstream ss; ss << "TLSStream: Unhandled Error Code " << errnumber << " (switchTLSError).";
00431                 ret = ss.str();
00432                 break;
00433         }
00434         }
00435 
00436         return ret;
00437     }
00438 
00439 }