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