summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorEric Milkie <milkie@10gen.com>2012-12-03 14:40:49 -0500
committerEric Milkie <milkie@10gen.com>2012-12-04 10:57:49 -0500
commite4de169d6ac4e34a5e30d6d70db7d32a55555467 (patch)
tree9e06cfc7b8cf9efd1b02c511ec33355a4fcb9a60 /src/mongo
parentddddf3b6a4bb3ff70fe12dd83e3ab0f43ffdd318 (diff)
downloadmongo-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.h6
-rw-r--r--src/mongo/util/net/message_server_port.cpp3
-rw-r--r--src/mongo/util/net/miniwebserver.cpp36
-rw-r--r--src/mongo/util/net/sock.cpp6
-rw-r--r--src/mongo/util/net/sock.h7
-rw-r--r--src/mongo/util/net/ssl_manager.cpp108
-rw-r--r--src/mongo/util/net/ssl_manager.h24
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