diff options
author | Eric Milkie <milkie@10gen.com> | 2012-12-03 14:40:49 -0500 |
---|---|---|
committer | Eric Milkie <milkie@10gen.com> | 2012-12-04 10:57:49 -0500 |
commit | e4de169d6ac4e34a5e30d6d70db7d32a55555467 (patch) | |
tree | 9e06cfc7b8cf9efd1b02c511ec33355a4fcb9a60 /src/mongo | |
parent | ddddf3b6a4bb3ff70fe12dd83e3ab0f43ffdd318 (diff) | |
download | mongo-e4de169d6ac4e34a5e30d6d70db7d32a55555467.tar.gz |
SERVER-7202 proper error handling framework for SSL
1. change "postFork()" to "doSSLHandshake()"
2. properly catch socket exceptions thrown by doSSLHandshake
3. properly handle error statuses from SSL_new, SSL_set_fd, SSL_connect,
SSL_accept
4. thread-safe implementation to fetch error text from SSL errors
(_getSSLErrorMessage)
5. check that private key and certificate match each other at startup time
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/util/net/message_port.h | 6 | ||||
-rw-r--r-- | src/mongo/util/net/message_server_port.cpp | 3 | ||||
-rw-r--r-- | src/mongo/util/net/miniwebserver.cpp | 36 | ||||
-rw-r--r-- | src/mongo/util/net/sock.cpp | 6 | ||||
-rw-r--r-- | src/mongo/util/net/sock.h | 7 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.cpp | 108 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.h | 24 |
7 files changed, 141 insertions, 49 deletions
diff --git a/src/mongo/util/net/message_port.h b/src/mongo/util/net/message_port.h index 8e822d43218..7f9681eb8eb 100644 --- a/src/mongo/util/net/message_port.h +++ b/src/mongo/util/net/message_port.h @@ -105,7 +105,11 @@ namespace mongo { return psock->connect( farEnd ); } #ifdef MONGO_SSL - /** secures inline */ + /** + * Initiates the TLS/SSL handshake on this MessagingPort. + * When this function returns, further communication on this + * MessagingPort will be encrypted. + */ void secure( SSLManager * ssl ) { psock->secure( ssl ); } diff --git a/src/mongo/util/net/message_server_port.cpp b/src/mongo/util/net/message_server_port.cpp index 7f2317b80cc..dc4b6c961b8 100644 --- a/src/mongo/util/net/message_server_port.cpp +++ b/src/mongo/util/net/message_server_port.cpp @@ -176,8 +176,6 @@ namespace mongo { inPort->psock->setLogLevel(1); scoped_ptr<MessagingPort> p( inPort ); - p->psock->postFork(); - string otherSide; Message m; @@ -187,6 +185,7 @@ namespace mongo { otherSide = p->psock->remoteString(); + p->psock->doSSLHandshake(); handler->connected( p.get() ); while ( ! inShutdown() ) { diff --git a/src/mongo/util/net/miniwebserver.cpp b/src/mongo/util/net/miniwebserver.cpp index 1e04693337f..dc890923bb0 100644 --- a/src/mongo/util/net/miniwebserver.cpp +++ b/src/mongo/util/net/miniwebserver.cpp @@ -109,25 +109,31 @@ namespace mongo { } void MiniWebServer::accepted(boost::shared_ptr<Socket> psock, long long connectionId ) { - psock->postFork(); - psock->setTimeout(8); char buf[4096]; int len = 0; - while ( 1 ) { - int left = sizeof(buf) - 1 - len; - if( left == 0 ) - break; - int x = psock->unsafe_recv( buf + len , left ); - if ( x <= 0 ) { - psock->close(); - return; - } - len += x; - buf[ len ] = 0; - if ( fullReceive( buf ) ) { - break; + try { + psock->doSSLHandshake(); + psock->setTimeout(8); + while ( 1 ) { + int left = sizeof(buf) - 1 - len; + if( left == 0 ) + break; + int x = psock->unsafe_recv( buf + len , left ); + if ( x <= 0 ) { + psock->close(); + return; + } + len += x; + buf[ len ] = 0; + if ( fullReceive( buf ) ) { + break; + } } } + catch (const SocketException& e) { + LOG(1) << "couldn't recv data via http client: " << e << endl; + return; + } buf[len] = 0; string responseMsg; diff --git a/src/mongo/util/net/sock.cpp b/src/mongo/util/net/sock.cpp index 958363bd2f9..5db9f1fcb07 100644 --- a/src/mongo/util/net/sock.cpp +++ b/src/mongo/util/net/sock.cpp @@ -436,7 +436,7 @@ namespace mongo { fassert(16504, !_ssl); fassert(16505, _fd >= 0); _ssl = ssl->secure( _fd ); - SSL_connect( _ssl ); + ssl->connect(_ssl); } void Socket::secureAccepted( SSLManager * ssl ) { @@ -444,12 +444,12 @@ namespace mongo { } #endif - void Socket::postFork() { + void Socket::doSSLHandshake() { #ifdef MONGO_SSL if ( _sslAccepted ) { fassert(16506, _fd); _ssl = _sslAccepted->secure( _fd ); - SSL_accept( _ssl ); + _sslAccepted->accept(_ssl); _sslAccepted = 0; } #endif diff --git a/src/mongo/util/net/sock.h b/src/mongo/util/net/sock.h index fd762f6959f..9dac476c712 100644 --- a/src/mongo/util/net/sock.h +++ b/src/mongo/util/net/sock.h @@ -219,9 +219,12 @@ namespace mongo { #endif /** - * call this after a fork for server sockets + * This function calls SSL_accept() if SSL-encrypted sockets + * are desired. SSL_accept() waits until the remote host calls + * SSL_connect(). + * This function may throw SocketException. */ - void postFork(); + void doSSLHandshake(); /** * @return the time when the socket was opened. diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp index 8681c9cfa0a..3fa68b4e632 100644 --- a/src/mongo/util/net/ssl_manager.cpp +++ b/src/mongo/util/net/ssl_manager.cpp @@ -25,6 +25,7 @@ #include "mongo/bson/util/atomic_int.h" #include "mongo/util/concurrency/mutex.h" #include "mongo/util/mongoutils/str.h" +#include "mongo/util/net/sock.h" namespace mongo { @@ -101,9 +102,6 @@ namespace mongo { //////////////////////////////////////////////////////////////// - - - SSLManager::SSLManager(bool client) { _client = client; SSL_library_init(); @@ -111,8 +109,10 @@ namespace mongo { ERR_load_crypto_strings(); _context = SSL_CTX_new( client ? SSLv23_client_method() : SSLv23_server_method() ); - massert( 15864 , mongoutils::str::stream() << "can't create SSL Context: " << - ERR_error_string(ERR_get_error(), NULL) , _context ); + massert(15864, + mongoutils::str::stream() << "can't create SSL Context: " << + _getSSLErrorMessage(ERR_get_error()), + _context); // Activate all bug workaround options, to support buggy client SSL's. SSL_CTX_set_options(_context, SSL_OP_ALL); @@ -125,20 +125,6 @@ namespace mongo { SSLThreadInfo::get(); } - void SSLManager::setupPubPriv(const std::string& privateKeyFile, const std::string& publicKeyFile) { - massert(15865, - mongoutils::str::stream() << "Can't read SSL certificate from file " - << publicKeyFile << ":" << ERR_error_string(ERR_get_error(), NULL) , - SSL_CTX_use_certificate_file(_context, publicKeyFile.c_str(), SSL_FILETYPE_PEM)); - - - massert(15866 , - mongoutils::str::stream() << "Can't read SSL private key from file " - << privateKeyFile << " : " << ERR_error_string(ERR_get_error(), NULL) , - SSL_CTX_use_PrivateKey_file(_context, privateKeyFile.c_str(), SSL_FILETYPE_PEM)); - } - - int SSLManager::password_cb(char *buf,int num, int rwflag,void *userdata) { SSLManager* sm = static_cast<SSLManager*>(userdata); std::string pass = sm->_password; @@ -150,7 +136,8 @@ namespace mongo { _password = password; if ( SSL_CTX_use_certificate_chain_file( _context , keyFile.c_str() ) != 1 ) { - log() << "Can't read certificate file: " << keyFile << endl; + log() << "Can't read certificate file: " << keyFile << " " << + _getSSLErrorMessage(ERR_get_error()) << endl; return false; } @@ -158,22 +145,95 @@ namespace mongo { SSL_CTX_set_default_passwd_cb( _context, &SSLManager::password_cb ); if ( SSL_CTX_use_PrivateKey_file( _context , keyFile.c_str() , SSL_FILETYPE_PEM ) != 1 ) { - log() << "Can't read key file: " << keyFile << endl; + log() << "Can't read key file: " << keyFile << " " << + _getSSLErrorMessage(ERR_get_error()) << endl; return false; } + // Verify that the certificate and the key go together. + if (SSL_CTX_check_private_key(_context) != 1) { + log() << "SSL certificate validation: " << _getSSLErrorMessage(ERR_get_error()) << endl; + return false; + } return true; } - + SSL * SSLManager::secure(int fd) { // This just ensures that SSL multithreading support is set up for this thread, // if it's not already. SSLThreadInfo::get(); SSL * ssl = SSL_new(_context); - massert( 15861 , "can't create SSL" , ssl ); - SSL_set_fd( ssl , fd ); + massert(15861, + _getSSLErrorMessage(ERR_get_error()), + ssl); + + int status = SSL_set_fd( ssl , fd ); + massert(16509, + _getSSLErrorMessage(ERR_get_error()), + status == 1); + return ssl; } + + void SSLManager::connect(SSL* ssl) { + int ret = SSL_connect(ssl); + if (ret != 1) + _handleSSLError(SSL_get_error(ssl, ret)); + } + + void SSLManager::accept(SSL* ssl) { + int ret = SSL_accept(ssl); + if (ret != 1) + _handleSSLError(SSL_get_error(ssl, ret)); + } + + std::string SSLManager::_getSSLErrorMessage(int code) { + // 120 from the SSL documentation for ERR_error_string + static const size_t msglen = 120; + + char msg[msglen]; + ERR_error_string_n(code, msg, msglen); + return msg; + } + + void SSLManager::_handleSSLError(int code) { + switch (code) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + // should not happen because we turned on AUTO_RETRY + log() << "SSL error" << endl; + throw SocketException(SocketException::CONNECT_ERROR, ""); + break; + + case SSL_ERROR_SYSCALL: + if (code < 0) { + log() << "socket error: " << errnoWithDescription() << endl; + throw SocketException(SocketException::CONNECT_ERROR, ""); + } + log() << "could not negotiate SSL connection: EOF detected" << endl; + throw SocketException(SocketException::CONNECT_ERROR, ""); + break; + + case SSL_ERROR_SSL: + { + int ret = ERR_get_error(); + log() << _getSSLErrorMessage(ret) << endl; + throw SocketException(SocketException::CONNECT_ERROR, ""); + break; + } + case SSL_ERROR_ZERO_RETURN: + log() << "could not negotiate SSL connection: EOF detected" << endl; + throw SocketException(SocketException::CONNECT_ERROR, ""); + break; + + default: + log() << "unrecognized SSL error" << endl; + throw SocketException(SocketException::CONNECT_ERROR, ""); + break; + } + } + } #endif + diff --git a/src/mongo/util/net/ssl_manager.h b/src/mongo/util/net/ssl_manager.h index 6c23e2be09f..aae55086512 100644 --- a/src/mongo/util/net/ssl_manager.h +++ b/src/mongo/util/net/ssl_manager.h @@ -29,16 +29,25 @@ namespace mongo { MONGO_DISALLOW_COPYING(SSLManager); public: SSLManager( bool client ); - + /** @return true if was successful, otherwise false */ bool setupPEM( const std::string& keyFile , const std::string& password ); - void setupPubPriv( const std::string& privateKeyFile , const std::string& publicKeyFile ); /** * creates an SSL context to be used for this file descriptor * caller should delete */ SSL * secure( int fd ); + + /** + * Initiates a TLS connection + */ + void connect(SSL* ssl); + + /** + * Waits for the other side to initiate a TLS connection + */ + void accept(SSL* ssl); static int password_cb( char *buf,int num, int rwflag,void *userdata ); @@ -46,6 +55,17 @@ namespace mongo { bool _client; SSL_CTX* _context; std::string _password; + + /** + * Fetches the error text for an error code, in a thread-safe manner. + */ + std::string _getSSLErrorMessage(int code); + + /** + * Given an error code from an SSL-type IO function, logs an + * appropriate message and throws a SocketException + */ + void _handleSSLError(int code); }; } #endif |