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