summaryrefslogtreecommitdiff
path: root/src/mongo/util/net/ssl_manager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/util/net/ssl_manager.cpp')
-rw-r--r--src/mongo/util/net/ssl_manager.cpp163
1 files changed, 101 insertions, 62 deletions
diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp
index 92a67e13b65..b78afab7ce9 100644
--- a/src/mongo/util/net/ssl_manager.cpp
+++ b/src/mongo/util/net/ssl_manager.cpp
@@ -65,6 +65,10 @@ namespace mongo {
SSLParams sslGlobalParams;
+const SSLParams& getSSLGlobalParams() {
+ return sslGlobalParams;
+}
+
#ifdef MONGO_CONFIG_SSL
// Old copies of OpenSSL will not have constants to disable protocols they don't support.
// Define them to values we can OR together safely to generically disable these protocols across
@@ -170,12 +174,21 @@ class SSLManager : public SSLManagerInterface {
public:
explicit SSLManager(const SSLParams& params, bool isServer);
+ /**
+ * Initializes an OpenSSL context according to the provided settings. Only settings which are
+ * acceptable on non-blocking connections are set.
+ */
+ Status initSSLContext(SSL_CTX* context, const SSLParams& params) final;
+
virtual SSLConnection* connect(Socket* socket);
virtual SSLConnection* accept(Socket* socket, const char* initialBytes, int len);
- virtual std::string parseAndValidatePeerCertificate(const SSLConnection* conn,
- const std::string& remoteHost);
+ virtual std::string parseAndValidatePeerCertificateDeprecated(const SSLConnection* conn,
+ const std::string& remoteHost);
+
+ StatusWith<boost::optional<std::string>> parseAndValidatePeerCertificate(
+ SSL* conn, const std::string& remoteHost) final;
virtual void cleanupThreadLocals();
@@ -219,9 +232,10 @@ private:
MONGO_COMPILER_NORETURN void _handleSSLError(int code, int ret);
/*
- * Init the SSL context using parameters provided in params.
+ * Init the SSL context using parameters provided in params. This SSL context will
+ * be configured for blocking send/receive.
*/
- bool _initSSLContext(UniqueSSLContext* context, const SSLParams& params);
+ bool _initSynchronousSSLContext(UniqueSSLContext* context, const SSLParams& params);
/*
* Converts time from OpenSSL return value to unsigned long long
@@ -323,8 +337,7 @@ MONGO_INITIALIZER(SetupOpenSSL)(InitializerContext*) {
return Status::OK();
}
-MONGO_INITIALIZER_WITH_PREREQUISITES(SSLManager, ("SetupOpenSSL"))
-(InitializerContext*) {
+MONGO_INITIALIZER_WITH_PREREQUISITES(SSLManager, ("SetupOpenSSL"))(InitializerContext*) {
stdx::lock_guard<SimpleMutex> lck(sslManagerMtx);
if (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled) {
theSSLManager = new SSLManager(sslGlobalParams, isSSLServer);
@@ -412,7 +425,7 @@ SSLManager::SSLManager(const SSLParams& params, bool isServer)
_weakValidation(params.sslWeakCertificateValidation),
_allowInvalidCertificates(params.sslAllowInvalidCertificates),
_allowInvalidHostnames(params.sslAllowInvalidHostnames) {
- if (!_initSSLContext(&_clientContext, params)) {
+ if (!_initSynchronousSSLContext(&_clientContext, params)) {
uasserted(16768, "ssl initialization problem");
}
@@ -434,7 +447,7 @@ SSLManager::SSLManager(const SSLParams& params, bool isServer)
}
// SSL server specific initialization
if (isServer) {
- if (!_initSSLContext(&_serverContext, params)) {
+ if (!_initSynchronousSSLContext(&_serverContext, params)) {
uasserted(16562, "ssl initialization problem");
}
@@ -511,19 +524,14 @@ void SSLManager::SSL_free(SSLConnection* conn) {
return ::SSL_free(conn->ssl);
}
-bool SSLManager::_initSSLContext(UniqueSSLContext* contextPtr, const SSLParams& params) {
- UniqueSSLContext context(SSL_CTX_new(SSLv23_method()), _free_ssl_context);
- massert(15864,
- mongoutils::str::stream()
- << "can't create SSL Context: " << getSSLErrorMessage(ERR_get_error()),
- context);
-
+Status SSLManager::initSSLContext(SSL_CTX* context, const SSLParams& params) {
// SSL_OP_ALL - Activate all bug workaround options, to support buggy client SSL's.
// SSL_OP_NO_SSLv2 - Disable SSL v2 support
// SSL_OP_NO_SSLv3 - Disable SSL v3 support
long supportedProtocols = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
- // Set the supported TLS protocols. Allow --sslDisabledProtocols to disable selected ciphers.
+ // Set the supported TLS protocols. Allow --sslDisabledProtocols to disable selected
+ // ciphers.
if (!params.sslDisabledProtocols.empty()) {
for (const SSLParams::Protocols& protocol : params.sslDisabledProtocols) {
if (protocol == SSLParams::Protocols::TLS1_0) {
@@ -535,7 +543,7 @@ bool SSLManager::_initSSLContext(UniqueSSLContext* contextPtr, const SSLParams&
}
}
}
- SSL_CTX_set_options(context.get(), supportedProtocols);
+ ::SSL_CTX_set_options(context, supportedProtocols);
// HIGH - Enable strong ciphers
// !EXPORT - Disable export ciphers (40/56 bit)
@@ -548,51 +556,58 @@ bool SSLManager::_initSSLContext(UniqueSSLContext* contextPtr, const SSLParams&
cipherConfig = params.sslCipherConfig;
}
- massert(28615,
- mongoutils::str::stream()
- << "can't set supported cipher suites: " << getSSLErrorMessage(ERR_get_error()),
- SSL_CTX_set_cipher_list(context.get(), cipherConfig.c_str()));
+ if (0 == ::SSL_CTX_set_cipher_list(context, cipherConfig.c_str())) {
+ return Status(ErrorCodes::InvalidSSLConfiguration,
+ str::stream() << "Can not set supported cipher suites: "
+ << getSSLErrorMessage(ERR_get_error()));
+ }
- // If renegotiation is needed, don't return from recv() or send() until it's successful.
- // Note: this is for blocking sockets only.
- SSL_CTX_set_mode(context.get(), SSL_MODE_AUTO_RETRY);
+ // We use the address of the context as the session id context.
+ if (0 == ::SSL_CTX_set_session_id_context(
+ context, reinterpret_cast<unsigned char*>(&context), sizeof(context))) {
+ return Status(ErrorCodes::InvalidSSLConfiguration,
+ str::stream() << "Can not store ssl session id context: "
+ << getSSLErrorMessage(ERR_get_error()));
+ }
- massert(28607,
- mongoutils::str::stream()
- << "can't store ssl session id context: " << getSSLErrorMessage(ERR_get_error()),
- SSL_CTX_set_session_id_context(
- context.get(),
- static_cast<unsigned char*>(static_cast<void*>(contextPtr)),
- sizeof(*contextPtr)));
-
- // Use the clusterfile for internal outgoing SSL connections if specified
- if (contextPtr == &_clientContext && !params.sslClusterFile.empty()) {
- EVP_set_pw_prompt("Enter cluster certificate passphrase");
- if (!_setupPEM(context.get(), params.sslClusterFile, params.sslClusterPassword)) {
- return false;
+ if (!params.sslClusterFile.empty()) {
+ ::EVP_set_pw_prompt("Enter cluster certificate passphrase");
+ if (!_setupPEM(context, params.sslClusterFile, params.sslClusterPassword)) {
+ return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up ssl clusterFile.");
}
- }
- // Use the pemfile for everything else
- else if (!params.sslPEMKeyFile.empty()) {
- EVP_set_pw_prompt("Enter PEM passphrase");
- if (!_setupPEM(context.get(), params.sslPEMKeyFile, params.sslPEMKeyPassword)) {
- return false;
+ } else if (!params.sslPEMKeyFile.empty()) {
+ // Use the pemfile for everything else
+ ::EVP_set_pw_prompt("Enter PEM passphrase");
+ if (!_setupPEM(context, params.sslPEMKeyFile, params.sslPEMKeyPassword)) {
+ return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up PEM key file.");
}
}
if (!params.sslCAFile.empty()) {
// Set up certificate validation with a certificate authority
- if (!_setupCA(context.get(), params.sslCAFile)) {
- return false;
+ if (!_setupCA(context, params.sslCAFile)) {
+ return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up CA file.");
}
}
if (!params.sslCRLFile.empty()) {
- if (!_setupCRL(context.get(), params.sslCRLFile)) {
- return false;
+ if (!_setupCRL(context, params.sslCRLFile)) {
+ return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up CRL file.");
}
}
+ return Status::OK();
+}
+
+bool SSLManager::_initSynchronousSSLContext(UniqueSSLContext* contextPtr, const SSLParams& params) {
+ UniqueSSLContext context(SSL_CTX_new(SSLv23_method()), _free_ssl_context);
+
+ uassertStatusOK(initSSLContext(context.get(), params));
+
+ // If renegotiation is needed, don't return from recv() or send() until it's successful.
+ // Note: this is for blocking sockets only.
+ SSL_CTX_set_mode(context.get(), SSL_MODE_AUTO_RETRY);
+
*contextPtr = std::move(context);
return true;
}
@@ -867,35 +882,38 @@ bool SSLManager::_hostNameMatch(const char* nameToMatch, const char* certHostNam
}
}
-std::string SSLManager::parseAndValidatePeerCertificate(const SSLConnection* conn,
- const std::string& remoteHost) {
+StatusWith<boost::optional<std::string>> SSLManager::parseAndValidatePeerCertificate(
+ SSL* conn, const std::string& remoteHost) {
// only set if a CA cert has been provided
if (!_sslConfiguration.hasCA)
- return "";
+ return {boost::none};
- X509* peerCert = SSL_get_peer_certificate(conn->ssl);
+ X509* peerCert = SSL_get_peer_certificate(conn);
if (NULL == peerCert) { // no certificate presented by peer
if (_weakValidation) {
warning() << "no SSL certificate provided by peer" << endl;
} else {
- error() << "no SSL certificate provided by peer; connection rejected" << endl;
- throw SocketException(SocketException::CONNECT_ERROR, "");
+ auto msg = "no SSL certificate provided by peer; connection rejected";
+ error() << msg;
+ return Status(ErrorCodes::SSLHandshakeFailed, msg);
}
- return "";
+ return {boost::none};
}
ON_BLOCK_EXIT(X509_free, peerCert);
- long result = SSL_get_verify_result(conn->ssl);
+ long result = SSL_get_verify_result(conn);
if (result != X509_V_OK) {
if (_allowInvalidCertificates) {
warning() << "SSL peer certificate validation failed:"
<< X509_verify_cert_error_string(result);
} else {
- error() << "SSL peer certificate validation failed:"
- << X509_verify_cert_error_string(result);
- throw SocketException(SocketException::CONNECT_ERROR, "");
+ str::stream msg;
+ msg << "SSL peer certificate validation failed:"
+ << X509_verify_cert_error_string(result);
+ error() << msg.ss.str();
+ return Status(ErrorCodes::SSLHandshakeFailed, msg);
}
}
@@ -905,7 +923,7 @@ std::string SSLManager::parseAndValidatePeerCertificate(const SSLConnection* con
// If this is an SSL client context (on a MongoDB server or client)
// perform hostname validation of the remote server
if (remoteHost.empty()) {
- return peerSubjectName;
+ return boost::make_optional(peerSubjectName);
}
// Try to match using the Subject Alternate Name, if it exists.
@@ -947,12 +965,25 @@ std::string SSLManager::parseAndValidatePeerCertificate(const SSLConnection* con
if (_allowInvalidCertificates || _allowInvalidHostnames) {
warning() << "The server certificate does not match the host name " << remoteHost;
} else {
- error() << "The server certificate does not match the host name " << remoteHost;
- throw SocketException(SocketException::CONNECT_ERROR, "");
+ str::stream msg;
+ msg << "The server certificate does not match the host name " << remoteHost;
+ error() << msg.ss.str();
+ return Status(ErrorCodes::SSLHandshakeFailed, msg);
}
}
- return peerSubjectName;
+ return boost::make_optional(peerSubjectName);
+}
+
+std::string SSLManager::parseAndValidatePeerCertificateDeprecated(const SSLConnection* conn,
+ const std::string& remoteHost) {
+ auto swPeerSubjectName = parseAndValidatePeerCertificate(conn->ssl, remoteHost);
+ // We can't use uassertStatusOK here because we need to throw a SocketException.
+ if (!swPeerSubjectName.isOK()) {
+ throw SocketException(SocketException::CONNECT_ERROR,
+ swPeerSubjectName.getStatus().reason());
+ }
+ return swPeerSubjectName.getValue().get_value_or("");
}
void SSLManager::cleanupThreadLocals() {
@@ -1007,5 +1038,13 @@ void SSLManager::_handleSSLError(int code, int ret) {
}
throw SocketException(SocketException::CONNECT_ERROR, "");
}
+#else
+
+MONGO_INITIALIZER(SSLManager)(InitializerContext*) {
+ // we need a no-op initializer so that we can depend on SSLManager as a prerequisite in
+ // non-SSL builds.
+ return Status::OK();
+}
+
#endif // #ifdef MONGO_CONFIG_SSL
}