summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2020-12-10 19:59:08 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-12-11 03:25:30 +0000
commit19ed9c958b369bd7e1776a57bd406ebe84cf2bec (patch)
tree92feec2a6104e28a340d2a8469220dbda4635a9f /src/mongo
parentdc77c3d344443071783d7098e75d2379bc749be3 (diff)
downloadmongo-19ed9c958b369bd7e1776a57bd406ebe84cf2bec.tar.gz
SERVER-52945 Make mongod use x509 auth on egress connections if NetworkInterface has SSLConnectionContext override even if other egress connections use keyFile auth
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/client/async_client.cpp7
-rw-r--r--src/mongo/client/async_client.h4
-rw-r--r--src/mongo/client/authenticate.cpp37
-rw-r--r--src/mongo/client/authenticate.h28
-rw-r--r--src/mongo/client/dbclient_base.cpp8
-rw-r--r--src/mongo/client/internal_auth.cpp14
-rw-r--r--src/mongo/client/internal_auth.h10
-rw-r--r--src/mongo/db/initialize_server_security_state.cpp12
-rw-r--r--src/mongo/db/mongod_options.cpp1
-rw-r--r--src/mongo/executor/connection_pool_tl.cpp78
-rw-r--r--src/mongo/util/net/SConscript2
-rw-r--r--src/mongo/util/net/network_interface_ssl_test.cpp28
-rw-r--r--src/mongo/util/net/ssl_manager.cpp7
-rw-r--r--src/mongo/util/net/ssl_manager_openssl.cpp173
-rw-r--r--src/mongo/util/net/ssl_manager_test.cpp10
-rw-r--r--src/mongo/util/net/ssl_parameters_auth.cpp4
16 files changed, 312 insertions, 111 deletions
diff --git a/src/mongo/client/async_client.cpp b/src/mongo/client/async_client.cpp
index f637a3c0f17..5037c621a04 100644
--- a/src/mongo/client/async_client.cpp
+++ b/src/mongo/client/async_client.cpp
@@ -165,7 +165,9 @@ Future<void> AsyncDBClient::authenticate(const BSONObj& params) {
return auth::authenticateClient(params, remote(), clientName, _makeAuthRunCommandHook());
}
-Future<void> AsyncDBClient::authenticateInternal(boost::optional<std::string> mechanismHint) {
+Future<void> AsyncDBClient::authenticateInternal(
+ boost::optional<std::string> mechanismHint,
+ std::shared_ptr<auth::InternalAuthParametersProvider> authProvider) {
// If no internal auth information is set, don't bother trying to authenticate.
if (!auth::isInternalAuthSet()) {
return Future<void>::makeReady();
@@ -182,7 +184,8 @@ Future<void> AsyncDBClient::authenticateInternal(boost::optional<std::string> me
return auth::authenticateInternalClient(clientName,
mechanismHint,
auth::StepDownBehavior::kKillConnection,
- _makeAuthRunCommandHook());
+ _makeAuthRunCommandHook(),
+ std::move(authProvider));
}
Future<bool> AsyncDBClient::completeSpeculativeAuth(std::shared_ptr<SaslClientSession> session,
diff --git a/src/mongo/client/async_client.h b/src/mongo/client/async_client.h
index 1bfcdad04bd..f586335949b 100644
--- a/src/mongo/client/async_client.h
+++ b/src/mongo/client/async_client.h
@@ -77,7 +77,9 @@ public:
Future<void> authenticate(const BSONObj& params);
- Future<void> authenticateInternal(boost::optional<std::string> mechanismHint);
+ Future<void> authenticateInternal(
+ boost::optional<std::string> mechanismHint,
+ std::shared_ptr<auth::InternalAuthParametersProvider> authProvider);
Future<bool> completeSpeculativeAuth(std::shared_ptr<SaslClientSession> session,
std::string authDB,
diff --git a/src/mongo/client/authenticate.cpp b/src/mongo/client/authenticate.cpp
index e280547fa5e..af3b48f73b1 100644
--- a/src/mongo/client/authenticate.cpp
+++ b/src/mongo/client/authenticate.cpp
@@ -35,8 +35,10 @@
#include "mongo/base/status.h"
#include "mongo/base/status_with.h"
+#include "mongo/bson/bsonobj.h"
#include "mongo/bson/json.h"
#include "mongo/bson/util/bson_extract.h"
+#include "mongo/client/internal_auth.h"
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/config.h"
#include "mongo/db/auth/authorization_manager.h"
@@ -138,7 +140,22 @@ Future<void> authX509(RunCommandHook runCommand, const BSONObj& params, StringDa
return runCommand(authRequest.getValue()).ignoreValue();
}
+class DefaultInternalAuthParametersProvider : public InternalAuthParametersProvider {
+public:
+ ~DefaultInternalAuthParametersProvider() = default;
+
+ BSONObj get(size_t index, StringData mechanism) final {
+ return getInternalAuthParams(index, mechanism);
+ }
+};
+
} // namespace
+
+std::shared_ptr<InternalAuthParametersProvider> createDefaultInternalAuthProvider() {
+ return std::make_shared<DefaultInternalAuthParametersProvider>();
+}
+
+
//
// General Auth
//
@@ -229,22 +246,26 @@ Future<std::string> negotiateSaslMechanism(RunCommandHook runCommand,
});
}
-Future<void> authenticateInternalClient(const std::string& clientSubjectName,
- boost::optional<std::string> mechanismHint,
- StepDownBehavior stepDownBehavior,
- RunCommandHook runCommand) {
+Future<void> authenticateInternalClient(
+ const std::string& clientSubjectName,
+ boost::optional<std::string> mechanismHint,
+ StepDownBehavior stepDownBehavior,
+ RunCommandHook runCommand,
+ std::shared_ptr<InternalAuthParametersProvider> internalParamsProvider) {
return negotiateSaslMechanism(
runCommand, internalSecurity.user->getName(), mechanismHint, stepDownBehavior)
- .then([runCommand, clientSubjectName](std::string mechanism) -> Future<void> {
- auto params = getInternalAuthParams(0, mechanism);
+ .then([runCommand, clientSubjectName, internalParamsProvider](
+ std::string mechanism) -> Future<void> {
+ auto params = internalParamsProvider->get(0, mechanism);
if (params.isEmpty()) {
return Status(ErrorCodes::BadValue,
"Missing authentication parameters for internal user auth");
}
return authenticateClient(params, HostAndPort(), clientSubjectName, runCommand)
.onError<ErrorCodes::AuthenticationFailed>(
- [runCommand, clientSubjectName, mechanism](Status status) -> Future<void> {
- auto altCreds = getInternalAuthParams(1, mechanism);
+ [runCommand, clientSubjectName, mechanism, internalParamsProvider](
+ Status status) -> Future<void> {
+ auto altCreds = internalParamsProvider->get(1, mechanism);
if (!altCreds.isEmpty()) {
return authenticateClient(
altCreds, HostAndPort(), clientSubjectName, runCommand);
diff --git a/src/mongo/client/authenticate.h b/src/mongo/client/authenticate.h
index 44d90eae612..8a3477da2c1 100644
--- a/src/mongo/client/authenticate.h
+++ b/src/mongo/client/authenticate.h
@@ -81,6 +81,24 @@ constexpr auto kAuthenticateCommand = "authenticate"_sd;
enum class StepDownBehavior { kKillConnection, kKeepConnectionOpen };
/**
+ * Provider of SASL credentials for internal authentication purposes.
+ */
+class InternalAuthParametersProvider {
+public:
+ virtual ~InternalAuthParametersProvider() = default;
+
+ /**
+ * Get the information for a given SASL mechanism.
+ *
+ * If there are multiple entries for a mechanism, suppots retrieval by index. Used when rotating
+ * the security key.
+ */
+ virtual BSONObj get(size_t index, StringData mechanism) = 0;
+};
+
+std::shared_ptr<InternalAuthParametersProvider> createDefaultInternalAuthProvider();
+
+/**
* Authenticate a user.
*
* Pass the default hostname for this client in through "hostname." If SSL is enabled and
@@ -126,10 +144,12 @@ Future<void> authenticateClient(const BSONObj& params,
* Because this may retry during cluster keyfile rollover, this may call the RunCommandHook more
* than once, but will only call the AuthCompletionHandler once.
*/
-Future<void> authenticateInternalClient(const std::string& clientSubjectName,
- boost::optional<std::string> mechanismHint,
- StepDownBehavior stepDownBehavior,
- RunCommandHook runCommand);
+Future<void> authenticateInternalClient(
+ const std::string& clientSubjectName,
+ boost::optional<std::string> mechanismHint,
+ StepDownBehavior stepDownBehavior,
+ RunCommandHook runCommand,
+ std::shared_ptr<InternalAuthParametersProvider> internalParamsProvider);
/**
* Build a BSONObject representing parameters to be passed to authenticateClient(). Takes
diff --git a/src/mongo/client/dbclient_base.cpp b/src/mongo/client/dbclient_base.cpp
index 5de427f1a83..96f057e118b 100644
--- a/src/mongo/client/dbclient_base.cpp
+++ b/src/mongo/client/dbclient_base.cpp
@@ -539,9 +539,11 @@ Status DBClientBase::authenticateInternalUser(auth::StepDownBehavior stepDownBeh
}
#endif
- auto status = auth::authenticateInternalClient(
- clientName, boost::none, stepDownBehavior, _makeAuthRunCommandHook())
- .getNoThrow();
+ auto authProvider = auth::createDefaultInternalAuthProvider();
+ auto status =
+ auth::authenticateInternalClient(
+ clientName, boost::none, stepDownBehavior, _makeAuthRunCommandHook(), authProvider)
+ .getNoThrow();
if (status.isOK()) {
return status;
}
diff --git a/src/mongo/client/internal_auth.cpp b/src/mongo/client/internal_auth.cpp
index 65fc3d60d5f..1effff0a08a 100644
--- a/src/mongo/client/internal_auth.cpp
+++ b/src/mongo/client/internal_auth.cpp
@@ -72,7 +72,19 @@ bool isInternalAuthSet() {
return internalAuthSet;
}
-BSONObj getInternalAuthParams(size_t idx, const std::string& mechanism) {
+BSONObj createInternalX509AuthDocument(boost::optional<StringData> userName) {
+ BSONObjBuilder builder;
+ builder.append(saslCommandMechanismFieldName, "MONGODB-X509");
+ builder.append(saslCommandUserDBFieldName, "$external");
+
+ if (userName) {
+ builder.append(saslCommandUserFieldName, userName.get());
+ }
+
+ return builder.obj();
+}
+
+BSONObj getInternalAuthParams(size_t idx, StringData mechanism) {
stdx::lock_guard<Latch> lk(internalAuthKeysMutex);
if (!internalAuthSet) {
return BSONObj();
diff --git a/src/mongo/client/internal_auth.h b/src/mongo/client/internal_auth.h
index e93b67ddde4..2ba33c9e136 100644
--- a/src/mongo/client/internal_auth.h
+++ b/src/mongo/client/internal_auth.h
@@ -69,7 +69,15 @@ bool isInternalAuthSet();
*/
std::string getInternalAuthDB();
-BSONObj getInternalAuthParams(size_t idx, const std::string& mechanism);
+/**
+ * Returns the internal auth sasl parameters.
+ */
+BSONObj getInternalAuthParams(size_t idx, StringData mechanism);
+
+/**
+ * Create a BSON document for internal authentication.
+ */
+BSONObj createInternalX509AuthDocument(boost::optional<StringData> userName = boost::none);
} // namespace auth
} // namespace mongo
diff --git a/src/mongo/db/initialize_server_security_state.cpp b/src/mongo/db/initialize_server_security_state.cpp
index 93b19abed90..46a08632239 100644
--- a/src/mongo/db/initialize_server_security_state.cpp
+++ b/src/mongo/db/initialize_server_security_state.cpp
@@ -62,13 +62,11 @@ bool initializeServerSecurityGlobalState(ServiceContext* service) {
#ifdef MONGO_CONFIG_SSL
if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509 ||
clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509) {
- auth::setInternalUserAuthParams(BSON(saslCommandMechanismFieldName
- << "MONGODB-X509" << saslCommandUserDBFieldName
- << "$external" << saslCommandUserFieldName
- << SSLManagerCoordinator::get()
- ->getSSLManager()
- ->getSSLConfiguration()
- .clientSubjectName.toString()));
+ auth::setInternalUserAuthParams(auth::createInternalX509AuthDocument(
+ boost::optional<StringData>{SSLManagerCoordinator::get()
+ ->getSSLManager()
+ ->getSSLConfiguration()
+ .clientSubjectName.toString()}));
}
#endif
diff --git a/src/mongo/db/mongod_options.cpp b/src/mongo/db/mongod_options.cpp
index db09e05615f..5b60719af64 100644
--- a/src/mongo/db/mongod_options.cpp
+++ b/src/mongo/db/mongod_options.cpp
@@ -510,6 +510,7 @@ Status storeMongodOptions(const moe::Environment& params) {
if (!replSettings.getReplSetString().empty() &&
(params.count("security.authorization") &&
params["security.authorization"].as<std::string>() == "enabled") &&
+ serverGlobalParams.clusterAuthMode.load() != ServerGlobalParams::ClusterAuthMode_x509 &&
!params.count("security.keyFile")) {
return Status(
ErrorCodes::BadValue,
diff --git a/src/mongo/executor/connection_pool_tl.cpp b/src/mongo/executor/connection_pool_tl.cpp
index dd04a67368b..8d2dc9f615c 100644
--- a/src/mongo/executor/connection_pool_tl.cpp
+++ b/src/mongo/executor/connection_pool_tl.cpp
@@ -34,6 +34,7 @@
#include "mongo/executor/connection_pool_tl.h"
#include "mongo/client/authenticate.h"
+#include "mongo/config.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/logv2/log.h"
@@ -163,10 +164,12 @@ void TLConnection::cancelTimeout() {
_timer->cancelTimeout();
}
+namespace {
+
class TLConnectionSetupHook : public executor::NetworkConnectionHook {
public:
- explicit TLConnectionSetupHook(executor::NetworkConnectionHook* hookToWrap)
- : _wrappedHook(hookToWrap) {}
+ explicit TLConnectionSetupHook(executor::NetworkConnectionHook* hookToWrap, bool x509AuthOnly)
+ : _wrappedHook(hookToWrap), _x509AuthOnly(x509AuthOnly) {}
BSONObj augmentIsMasterRequest(BSONObj cmdObj) override {
BSONObjBuilder bob(std::move(cmdObj));
@@ -174,7 +177,12 @@ public:
if (internalSecurity.user) {
bob.append("saslSupportedMechs", internalSecurity.user->getName().getUnambiguousName());
}
- _speculativeAuthType = auth::speculateInternalAuth(&bob, &_session);
+
+ if (_x509AuthOnly) {
+ _speculativeAuthType = auth::SpeculativeAuthType::kAuthenticate;
+ } else {
+ _speculativeAuthType = auth::speculateInternalAuth(&bob, &_session);
+ }
return bob.obj();
}
@@ -184,11 +192,17 @@ public:
const RemoteCommandResponse& isMasterReply) override try {
const auto& reply = isMasterReply.data;
- const auto saslMechsElem = reply.getField("saslSupportedMechs");
- if (saslMechsElem.type() == Array) {
- auto array = saslMechsElem.Array();
- for (const auto& elem : array) {
- _saslMechsForInternalAuth.push_back(elem.checkAndGetStringData().toString());
+ // X.509 auth only means we only want to use a single mechanism regards of what hello says
+ if (_x509AuthOnly) {
+ _saslMechsForInternalAuth.clear();
+ _saslMechsForInternalAuth.push_back("MONGODB-X509");
+ } else {
+ const auto saslMechsElem = reply.getField("saslSupportedMechs");
+ if (saslMechsElem.type() == Array) {
+ auto array = saslMechsElem.Array();
+ for (const auto& elem : array) {
+ _saslMechsForInternalAuth.push_back(elem.checkAndGetStringData().toString());
+ }
}
}
@@ -245,8 +259,39 @@ private:
auth::SpeculativeAuthType _speculativeAuthType;
BSONObj _speculativeAuthenticate;
executor::NetworkConnectionHook* const _wrappedHook = nullptr;
+ bool _x509AuthOnly;
};
+#ifdef MONGO_CONFIG_SSL
+class TransientInternalAuthParametersProvider : public auth::InternalAuthParametersProvider {
+public:
+ TransientInternalAuthParametersProvider(
+ const std::shared_ptr<const transport::SSLConnectionContext> transientSSLContext)
+ : _transientSSLContext(transientSSLContext) {}
+
+ ~TransientInternalAuthParametersProvider() = default;
+
+ BSONObj get(size_t index, StringData mechanism) final {
+ if (_transientSSLContext) {
+ if (index == 0) {
+ return auth::createInternalX509AuthDocument(
+ boost::optional<StringData>{_transientSSLContext->manager->getSSLConfiguration()
+ .clientSubjectName.toString()});
+ } else {
+ return BSONObj();
+ }
+ }
+
+ return auth::getInternalAuthParams(index, mechanism);
+ }
+
+private:
+ const std::shared_ptr<const transport::SSLConnectionContext> _transientSSLContext;
+};
+#endif
+
+} // namespace
+
void TLConnection::setup(Milliseconds timeout, SetupCallback cb) {
auto anchor = shared_from_this();
@@ -269,7 +314,18 @@ void TLConnection::setup(Milliseconds timeout, SetupCallback cb) {
}
});
- auto isMasterHook = std::make_shared<TLConnectionSetupHook>(_onConnectHook);
+#ifdef MONGO_CONFIG_SSL
+ bool x509AuthOnly =
+ _transientSSLContext.get() && _transientSSLContext->targetClusterURI.has_value();
+ auto authParametersProvider =
+ std::make_shared<TransientInternalAuthParametersProvider>(_transientSSLContext);
+#else
+ bool x509AuthOnly = false;
+ auto authParametersProvider = auth::createDefaultInternalAuthProvider();
+#endif
+
+ // For transient connections, only use X.509 auth.
+ auto isMasterHook = std::make_shared<TLConnectionSetupHook>(_onConnectHook, x509AuthOnly);
AsyncDBClient::connect(
_peer, _sslMode, _serviceContext, _reactor, timeout, _transientSSLContext)
@@ -291,7 +347,7 @@ void TLConnection::setup(Milliseconds timeout, SetupCallback cb) {
isMasterHook->getSpeculativeAuthenticateReply(),
isMasterHook->getSpeculativeAuthType());
})
- .then([this, isMasterHook](bool authenticatedDuringConnect) {
+ .then([this, isMasterHook, authParametersProvider](bool authenticatedDuringConnect) {
if (_skipAuth || authenticatedDuringConnect) {
return Future<void>::makeReady();
}
@@ -299,7 +355,7 @@ void TLConnection::setup(Milliseconds timeout, SetupCallback cb) {
boost::optional<std::string> mechanism;
if (!isMasterHook->saslMechsForInternalAuth().empty())
mechanism = isMasterHook->saslMechsForInternalAuth().front();
- return _client->authenticateInternal(std::move(mechanism));
+ return _client->authenticateInternal(std::move(mechanism), authParametersProvider);
})
.then([this] {
if (!_onConnectHook) {
diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript
index 71c46e22df6..80ba93870ed 100644
--- a/src/mongo/util/net/SConscript
+++ b/src/mongo/util/net/SConscript
@@ -249,6 +249,8 @@ if get_option('ssl') == 'on':
],
LIBDEPS=[
'$BUILD_DIR/mongo/client/connection_string',
+ '$BUILD_DIR/mongo/db/auth/builtin_roles',
+ '$BUILD_DIR/mongo/db/auth/user',
'$BUILD_DIR/mongo/executor/network_interface',
'$BUILD_DIR/mongo/executor/network_interface_factory',
'$BUILD_DIR/mongo/executor/network_interface_fixture',
diff --git a/src/mongo/util/net/network_interface_ssl_test.cpp b/src/mongo/util/net/network_interface_ssl_test.cpp
index 8e3b446bdfe..b6eeebf1295 100644
--- a/src/mongo/util/net/network_interface_ssl_test.cpp
+++ b/src/mongo/util/net/network_interface_ssl_test.cpp
@@ -29,21 +29,23 @@
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest
-#include <fstream>
-
#include "mongo/platform/basic.h"
+#include <fstream>
+
+#include "mongo/client/authenticate.h"
+#include "mongo/db/auth/authorization_session_impl.h"
#include "mongo/executor/network_interface_integration_fixture.h"
#include "mongo/logv2/log.h"
#include "mongo/unittest/integration_test.h"
#include "mongo/unittest/unittest.h"
-#include "mongo/util/assert_util.h"
+#include "mongo/util/net/ssl_options.h"
namespace mongo {
namespace executor {
namespace {
-std::string LoadFile(const std::string& name) {
+std::string loadFile(const std::string& name) {
std::ifstream input(name);
std::string str((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
return str;
@@ -52,10 +54,26 @@ std::string LoadFile(const std::string& name) {
class NetworkInterfaceSSLFixture : public NetworkInterfaceIntegrationFixture {
public:
void setUp() final {
+
+ // Setup an internal user so that we can use it for external auth
+ UserHandle user(User(UserName("__system", "local")));
+
+ internalSecurity.user = user;
+
+ // Force all connections to use SSL for outgoing
+ sslGlobalParams.sslMode.store(static_cast<int>(SSLParams::SSLModes::SSLMode_requireSSL));
+ sslGlobalParams.sslCAFile = "jstests/libs/ca.pem";
+ // Set a client cert that should be ignored if we use the transient cert correctly.
+ sslGlobalParams.sslPEMKeyFile = "jstests/libs/client.pem";
+
+ // Set the internal user auth parameters so we auth with X.509 externally
+ auth::setInternalUserAuthParams(
+ auth::createInternalX509AuthDocument(boost::optional<StringData>("Ignored")));
+
ConnectionPool::Options options;
options.transientSSLParams.emplace([] {
TransientSSLParams params;
- params.sslClusterPEMPayload = LoadFile("jstests/libs/client.pem");
+ params.sslClusterPEMPayload = loadFile("jstests/libs/server.pem");
params.targetedClusterConnectionString = ConnectionString::forLocal();
return params;
}());
diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp
index 39228cdfb8c..d80d882fe87 100644
--- a/src/mongo/util/net/ssl_manager.cpp
+++ b/src/mongo/util/net/ssl_manager.cpp
@@ -372,11 +372,8 @@ void SSLManagerCoordinator::rotate() {
int clusterAuthMode = serverGlobalParams.clusterAuthMode.load();
if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509 ||
clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509) {
- auth::setInternalUserAuthParams(
- BSON(saslCommandMechanismFieldName
- << "MONGODB-X509" << saslCommandUserDBFieldName << "$external"
- << saslCommandUserFieldName
- << manager->getSSLConfiguration().clientSubjectName.toString()));
+ auth::setInternalUserAuthParams(auth::createInternalX509AuthDocument(
+ StringData(manager->getSSLConfiguration().clientSubjectName.toString())));
}
auto tl = getGlobalServiceContext()->getTransportLayer();
diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp
index 1cba80be3a4..a0ef1ec1e42 100644
--- a/src/mongo/util/net/ssl_manager_openssl.cpp
+++ b/src/mongo/util/net/ssl_manager_openssl.cpp
@@ -97,6 +97,8 @@ int SSL_CTX_set_ciphersuites(SSL_CTX*, const char*) {
}
#endif
+using namespace fmt::literals;
+
namespace mongo {
namespace {
@@ -1128,7 +1130,7 @@ class SSLManagerOpenSSL : public SSLManagerInterface,
public std::enable_shared_from_this<SSLManagerOpenSSL> {
public:
explicit SSLManagerOpenSSL(const SSLParams& params, bool isServer);
- ~SSLManagerOpenSSL() final {
+ ~SSLManagerOpenSSL() {
stopJobs();
}
@@ -1292,13 +1294,23 @@ private:
* @param subjectName as a pointer to the subject name variable being set.
* @param serverNotAfter a Date_t object pointer that is valued if the
* date is to be checked (as for a server certificate) and null otherwise.
- * @return bool showing if the function was successful.
+ * @return Status::Ok showing if the function was successful.
*/
- bool _parseAndValidateCertificate(const std::string& keyFile,
- PasswordFetcher* keyPassword,
- SSLX509Name* subjectName,
- Date_t* serverNotAfter);
-
+ Status _parseAndValidateCertificate(const std::string& keyFile,
+ PasswordFetcher* keyPassword,
+ SSLX509Name* subjectName,
+ Date_t* serverNotAfter);
+
+ Status _parseAndValidateCertificateFromBIO(UniqueBIO inBio,
+ PasswordFetcher* keyPassword,
+ StringData fileNameForLogging,
+ SSLX509Name* subjectName,
+ Date_t* serverNotAfter);
+
+ Status _parseAndValidateCertificateFromMemory(StringData buffer,
+ PasswordFetcher* keyPassword,
+ SSLX509Name* subjectName,
+ Date_t* serverNotAfter);
/*
* Parse and return x509 object from the provided keyfile.
* @param keyFile referencing the PEM file to be read.
@@ -1554,11 +1566,13 @@ SSLManagerOpenSSL::SSLManagerOpenSSL(const SSLParams& params, bool isServer)
}
if (!clientPEM.empty()) {
- if (!_parseAndValidateCertificate(
- clientPEM, clientPassword, &_sslConfiguration.clientSubjectName, nullptr)) {
- uasserted(16941, "ssl initialization problem");
- }
+ auto status = _parseAndValidateCertificate(
+ clientPEM, clientPassword, &_sslConfiguration.clientSubjectName, nullptr);
+ uassertStatusOKWithContext(
+ status,
+ str::stream() << "ssl client initialization problem for certificate: " << clientPEM);
}
+
// SSL server specific initialization
if (isServer) {
if (!_initSynchronousSSLContext(&_serverContext, params, ConnectionDirection::kIncoming)) {
@@ -1566,12 +1580,15 @@ SSLManagerOpenSSL::SSLManagerOpenSSL(const SSLParams& params, bool isServer)
}
SSLX509Name serverSubjectName;
- if (!_parseAndValidateCertificate(params.sslPEMKeyFile,
- &_serverPEMPassword,
- &serverSubjectName,
- &_sslConfiguration.serverCertificateExpirationDate)) {
- uasserted(16942, "ssl initialization problem");
- }
+ auto status =
+ _parseAndValidateCertificate(params.sslPEMKeyFile,
+ &_serverPEMPassword,
+ &serverSubjectName,
+ &_sslConfiguration.serverCertificateExpirationDate);
+ uassertStatusOKWithContext(status,
+ str::stream()
+ << "ssl server initialization problem for certificate: "
+ << params.sslPEMKeyFile);
uassertStatusOK(_sslConfiguration.setServerSubjectName(std::move(serverSubjectName)));
@@ -2187,6 +2204,15 @@ Status SSLManagerOpenSSL::initSSLContext(SSL_CTX* context,
str::stream() << "Can not set up transient ssl cluster certificate for "
<< transientParams.targetedClusterConnectionString);
}
+
+ auto status = _parseAndValidateCertificateFromMemory(transientParams.sslClusterPEMPayload,
+ &_clusterPEMPassword,
+ &_sslConfiguration.clientSubjectName,
+ nullptr);
+ if (!status.isOK()) {
+ return status.withContext("Could not validate transient certificate");
+ }
+
} else if (direction == ConnectionDirection::kOutgoing && params.tlsWithholdClientCertificate) {
// Do not send a client certificate if they have been suppressed.
@@ -2294,60 +2320,97 @@ bool SSLManagerOpenSSL::_initSynchronousSSLContext(UniqueSSLContext* contextPtr,
return true;
}
-bool SSLManagerOpenSSL::_parseAndValidateCertificate(const std::string& keyFile,
- PasswordFetcher* keyPassword,
- SSLX509Name* subjectName,
- Date_t* serverCertificateExpirationDate) {
- BIO* inBIO = BIO_new(BIO_s_file());
- if (inBIO == nullptr) {
- LOGV2_ERROR(23243,
- "Failed to allocate BIO object",
- "error"_attr = getSSLErrorMessage(ERR_get_error()));
- return false;
+Status SSLManagerOpenSSL::_parseAndValidateCertificate(const std::string& keyFile,
+ PasswordFetcher* keyPassword,
+ SSLX509Name* subjectName,
+ Date_t* serverCertificateExpirationDate) {
+ UniqueBIO inBio(BIO_new(BIO_s_file()));
+ if (!inBio) {
+ return Status(
+ ErrorCodes::InvalidSSLConfiguration,
+ "Failed to allocate BIO object. error: {}"_format(getSSLErrorMessage(ERR_get_error())));
}
- ON_BLOCK_EXIT([&] { BIO_free(inBIO); });
- if (BIO_read_filename(inBIO, keyFile.c_str()) <= 0) {
- LOGV2_ERROR(23244,
- "Cannot read key file when setting subject name",
- "keyFile"_attr = keyFile,
- "error"_attr = getSSLErrorMessage(ERR_get_error()));
- return false;
+ if (BIO_read_filename(inBio.get(), keyFile.c_str()) <= 0) {
+ return Status(ErrorCodes::InvalidSSLConfiguration,
+ "Cannot read key file '{}' when setting subject name. error: {}"_format(
+ keyFile, getSSLErrorMessage(ERR_get_error())));
+ }
+
+ return _parseAndValidateCertificateFromBIO(
+ std::move(inBio), keyPassword, keyFile, subjectName, serverCertificateExpirationDate);
+}
+
+Status SSLManagerOpenSSL::_parseAndValidateCertificateFromMemory(
+ StringData buffer,
+ PasswordFetcher* keyPassword,
+ SSLX509Name* subjectName,
+ Date_t* serverCertificateExpirationDate) {
+ logv2::DynamicAttributes errorAttrs;
+
+#if OPENSSL_VERSION_NUMBER <= 0x1000114fL
+ UniqueBIO inBio(BIO_new_mem_buf(const_cast<char*>(buffer.rawData()), buffer.size()));
+#else
+ UniqueBIO inBio(BIO_new_mem_buf(buffer.rawData(), buffer.size()));
+#endif
+
+ if (!inBio) {
+ CaptureSSLErrorInAttrs capture(errorAttrs);
+ return Status(ErrorCodes::InvalidSSLConfiguration,
+ "Failed to allocate BIO object from in-memory payload. error: {}"_format(
+ getSSLErrorMessage(ERR_get_error())));
}
+ return _parseAndValidateCertificateFromBIO(std::move(inBio),
+ keyPassword,
+ "transient"_sd,
+ subjectName,
+ serverCertificateExpirationDate);
+}
+
+Status SSLManagerOpenSSL::_parseAndValidateCertificateFromBIO(
+ UniqueBIO inBio,
+ PasswordFetcher* keyPassword,
+ StringData fileNameForLogging,
+ SSLX509Name* subjectName,
+ Date_t* serverCertificateExpirationDate) {
X509* x509 = PEM_read_bio_X509(
- inBIO, nullptr, &SSLManagerOpenSSL::password_cb, static_cast<void*>(&keyPassword));
+ inBio.get(), nullptr, &SSLManagerOpenSSL::password_cb, static_cast<void*>(&keyPassword));
if (x509 == nullptr) {
- LOGV2_ERROR(23245,
- "Cannot retrieve certificate from keyfile",
- "keyFile"_attr = keyFile,
- "error"_attr = getSSLErrorMessage(ERR_get_error()));
- return false;
+ return Status(
+ ErrorCodes::InvalidSSLConfiguration,
+ "Cannot retrieve certificate from keyfile '{}' when setting subject name. error: {}"_format(
+ fileNameForLogging, getSSLErrorMessage(ERR_get_error())));
}
ON_BLOCK_EXIT([&] { X509_free(x509); });
*subjectName = getCertificateSubjectX509Name(x509);
- if (serverCertificateExpirationDate != nullptr) {
- auto notBeforeMillis = convertASN1ToMillis(X509_get_notBefore(x509));
- if (notBeforeMillis == Date_t()) {
- LOGV2_ERROR(23873, "date conversion failed");
- return false;
- }
- auto notAfterMillis = convertASN1ToMillis(X509_get_notAfter(x509));
- if (notAfterMillis == Date_t()) {
- LOGV2_ERROR(23874, "date conversion failed");
- return false;
- }
+ auto notBeforeMillis = convertASN1ToMillis(X509_get_notBefore(x509));
+ if (notBeforeMillis == Date_t()) {
+ return Status(ErrorCodes::InvalidSSLConfiguration,
+ "notBefore certificate date conversion failed");
+ }
- if ((notBeforeMillis > Date_t::now()) || (Date_t::now() > notAfterMillis)) {
- LOGV2_FATAL_NOTRACE(28652, "The provided SSL certificate is expired or not yet valid.");
- }
+ auto notAfterMillis = convertASN1ToMillis(X509_get_notAfter(x509));
+ if (notAfterMillis == Date_t()) {
+ return Status(ErrorCodes::InvalidSSLConfiguration,
+ "notAfter certificate date conversion failed");
+ }
+ auto now = Date_t::now();
+ if ((notBeforeMillis > now) || (now > notAfterMillis)) {
+ return Status(
+ ErrorCodes::InvalidSSLConfiguration,
+ "The provided SSL certificate is expired or not yet valid. notBefore {}, notAfter {}"_format(
+ notBeforeMillis.toString(), notAfterMillis.toString()));
+ }
+
+ if (serverCertificateExpirationDate != nullptr) {
*serverCertificateExpirationDate = notAfterMillis;
}
- return true;
+ return Status::OK();
}
// static
diff --git a/src/mongo/util/net/ssl_manager_test.cpp b/src/mongo/util/net/ssl_manager_test.cpp
index 1fbd42bde40..60349b4b2c2 100644
--- a/src/mongo/util/net/ssl_manager_test.cpp
+++ b/src/mongo/util/net/ssl_manager_test.cpp
@@ -109,7 +109,7 @@ private:
transport::TransportLayer* _transport = nullptr;
};
-std::string LoadFile(const std::string& name) {
+std::string loadFile(const std::string& name) {
std::ifstream input(name);
std::string str((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
return str;
@@ -521,7 +521,7 @@ TEST(SSLManager, InitContextFromFileShouldFail) {
// TODO SERVER-52858: there is no exception on Mac & Windows.
ASSERT_THROWS_CODE([&params] { SSLManagerInterface::create(params, true /* isSSLServer */); }(),
DBException,
- 16942);
+ ErrorCodes::InvalidSSLConfiguration);
#endif
}
@@ -571,7 +571,7 @@ TEST(SSLManager, InitContextFromMemory) {
params.sslCAFile = "jstests/libs/ca.pem";
TransientSSLParams transientParams;
- transientParams.sslClusterPEMPayload = LoadFile("jstests/libs/client.pem");
+ transientParams.sslClusterPEMPayload = loadFile("jstests/libs/client.pem");
std::shared_ptr<SSLManagerInterface> manager =
SSLManagerInterface::create(params, false /* isSSLServer */);
@@ -590,7 +590,7 @@ TEST(SSLManager, InitServerSideContextFromMemory) {
params.sslCAFile = "jstests/libs/ca.pem";
TransientSSLParams transientParams;
- transientParams.sslClusterPEMPayload = LoadFile("jstests/libs/client.pem");
+ transientParams.sslClusterPEMPayload = loadFile("jstests/libs/client.pem");
std::shared_ptr<SSLManagerInterface> manager =
SSLManagerInterface::create(params, true /* isSSLServer */);
@@ -622,7 +622,7 @@ TEST(SSLManager, TransientSSLParams) {
transport::TransportLayerASIO tla(options, &sepu);
TransientSSLParams transientSSLParams;
- transientSSLParams.sslClusterPEMPayload = LoadFile("jstests/libs/client.pem");
+ transientSSLParams.sslClusterPEMPayload = loadFile("jstests/libs/client.pem");
transientSSLParams.targetedClusterConnectionString = ConnectionString::forLocal();
auto result = tla.createTransientSSLContext(transientSSLParams, manager.get());
diff --git a/src/mongo/util/net/ssl_parameters_auth.cpp b/src/mongo/util/net/ssl_parameters_auth.cpp
index 612c2bc70cc..980c93df358 100644
--- a/src/mongo/util/net/ssl_parameters_auth.cpp
+++ b/src/mongo/util/net/ssl_parameters_auth.cpp
@@ -99,9 +99,7 @@ Status ClusterAuthModeServerParameter::setFromString(const std::string& strMode)
"connections"};
}
serverGlobalParams.clusterAuthMode.store(mode);
- auth::setInternalUserAuthParams(BSON(saslCommandMechanismFieldName
- << "MONGODB-X509" << saslCommandUserDBFieldName
- << "$external"));
+ auth::setInternalUserAuthParams(auth::createInternalX509AuthDocument());
} else if ((mode == ServerGlobalParams::ClusterAuthMode_x509) &&
(oldMode == ServerGlobalParams::ClusterAuthMode_sendX509)) {
serverGlobalParams.clusterAuthMode.store(mode);