|
IBR-DTNSuite 0.6
|
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 }