summaryrefslogtreecommitdiff
path: root/src/mongo/client/authenticate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/client/authenticate.cpp')
-rw-r--r--src/mongo/client/authenticate.cpp232
1 files changed, 152 insertions, 80 deletions
diff --git a/src/mongo/client/authenticate.cpp b/src/mongo/client/authenticate.cpp
index e32164a9228..6dc0337d627 100644
--- a/src/mongo/client/authenticate.cpp
+++ b/src/mongo/client/authenticate.cpp
@@ -40,9 +40,12 @@
#include "mongo/bson/util/bson_extract.h"
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/config.h"
+#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/sasl_command_constants.h"
#include "mongo/db/server_options.h"
#include "mongo/rpc/get_status_from_command_result.h"
+#include "mongo/rpc/op_msg_rpc_impls.h"
+#include "mongo/stdx/mutex.h"
#include "mongo/util/log.h"
#include "mongo/util/net/ssl_manager.h"
#include "mongo/util/net/ssl_options.h"
@@ -56,21 +59,11 @@ using executor::RemoteCommandResponse;
using AuthRequest = StatusWith<RemoteCommandRequest>;
-const char* const kMechanismMongoCR = "MONGODB-CR";
-const char* const kMechanismMongoX509 = "MONGODB-X509";
-const char* const kMechanismSaslPlain = "PLAIN";
-const char* const kMechanismGSSAPI = "GSSAPI";
-const char* const kMechanismScramSha1 = "SCRAM-SHA-1";
-
namespace {
const char* const kUserSourceFieldName = "userSource";
const BSONObj kGetNonceCmd = BSON("getnonce" << 1);
-bool isOk(const BSONObj& o) {
- return getStatusFromCommandResult(o).isOK();
-}
-
StatusWith<std::string> extractDBField(const BSONObj& params) {
std::string db;
if (params.hasField(kUserSourceFieldName)) {
@@ -90,15 +83,16 @@ StatusWith<std::string> extractDBField(const BSONObj& params) {
// MONGODB-CR
//
-void authMongoCRImpl(RunCommandHook cmd, const BSONObj& params, AuthCompletionHandler handler) {
- handler({ErrorCodes::AuthenticationFailed, "MONGODB-CR support was removed in MongoDB 3.8"});
+Future<void> authMongoCRImpl(RunCommandHook cmd, const BSONObj& params) {
+ return Status(ErrorCodes::AuthenticationFailed,
+ "MONGODB-CR support was removed in MongoDB 4.0");
}
//
// X-509
//
-AuthRequest createX509AuthCmd(const BSONObj& params, StringData clientName) {
+StatusWith<OpMsgRequest> createX509AuthCmd(const BSONObj& params, StringData clientName) {
if (clientName.empty()) {
return {ErrorCodes::AuthenticationFailed,
"Please enable SSL on the client-side to use the MONGODB-X509 authentication "
@@ -108,9 +102,6 @@ AuthRequest createX509AuthCmd(const BSONObj& params, StringData clientName) {
if (!db.isOK())
return std::move(db.getStatus());
- auto request = RemoteCommandRequest();
- request.dbname = db.getValue();
-
std::string username;
auto response = bsonExtractStringFieldWithDefault(
params, saslCommandUserFieldName, clientName.toString(), &username);
@@ -126,115 +117,196 @@ AuthRequest createX509AuthCmd(const BSONObj& params, StringData clientName) {
return {ErrorCodes::AuthenticationFailed, message.str()};
}
- request.cmdObj = BSON("authenticate" << 1 << "mechanism"
- << "MONGODB-X509"
- << "user"
- << username);
- return std::move(request);
+ return OpMsgRequest::fromDBAndBody(db.getValue(),
+ BSON("authenticate" << 1 << "mechanism"
+ << "MONGODB-X509"
+ << "user"
+ << username));
}
// Use the MONGODB-X509 protocol to authenticate as "username." The certificate details
// have already been communicated automatically as part of the connect call.
-void authX509(RunCommandHook runCommand,
- const BSONObj& params,
- StringData clientName,
- AuthCompletionHandler handler) {
+Future<void> authX509(RunCommandHook runCommand, const BSONObj& params, StringData clientName) {
invariant(runCommand);
- invariant(handler);
// Just 1 step: send authenticate command, receive response
auto authRequest = createX509AuthCmd(params, clientName);
if (!authRequest.isOK())
- return handler(std::move(authRequest.getStatus()));
+ return authRequest.getStatus();
- runCommand(authRequest.getValue(), handler);
+ // The runCommand hook checks whether the command returned { ok: 1.0 }, and we don't need to
+ // extract anything from the command payload, so this is just turning a Future<BSONObj>
+ // into a Future<void>
+ return runCommand(authRequest.getValue()).then([](BSONObj obj) { return Status::OK(); });
}
+} // namespace
//
// General Auth
//
-bool isFailedAuthOk(const AuthResponse& response) {
- return (response.status == ErrorCodes::AuthenticationFailed &&
- serverGlobalParams.transitionToAuth);
-}
-
-void auth(RunCommandHook runCommand,
- const BSONObj& params,
- const HostAndPort& hostname,
- StringData clientName,
- AuthCompletionHandler handler) {
+Future<void> authenticateClient(const BSONObj& params,
+ const HostAndPort& hostname,
+ const std::string& clientName,
+ RunCommandHook runCommand) {
std::string mechanism;
- auto authCompletionHandler = [handler](AuthResponse response) {
- if (isFailedAuthOk(response)) {
+ auto errorHandler = [](Status status) {
+ if (serverGlobalParams.transitionToAuth && !status.isA<ErrorCategory::NetworkError>()) {
// If auth failed in transitionToAuth, just pretend it succeeded.
log() << "Failed to authenticate in transitionToAuth, falling back to no "
"authentication.";
- // We need to mock a successful AuthResponse.
- return handler(AuthResponse(RemoteCommandResponse(BSON("ok" << 1), Milliseconds(0))));
+ return Status::OK();
}
- // otherwise, call handler
- return handler(std::move(response));
+ return status;
};
auto response = bsonExtractStringField(params, saslCommandMechanismFieldName, &mechanism);
if (!response.isOK())
- return handler(std::move(response));
+ return response;
if (params.hasField(saslCommandUserDBFieldName) && params.hasField(kUserSourceFieldName)) {
- return handler({ErrorCodes::AuthenticationFailed,
- "You cannot specify both 'db' and 'userSource'. Please use only 'db'."});
+ return Status(ErrorCodes::AuthenticationFailed,
+ "You cannot specify both 'db' and 'userSource'. Please use only 'db'.");
}
if (mechanism == kMechanismMongoCR)
- return authMongoCR(runCommand, params, authCompletionHandler);
+ return authMongoCR(runCommand, params).onError(errorHandler);
#ifdef MONGO_CONFIG_SSL
else if (mechanism == kMechanismMongoX509)
- return authX509(runCommand, params, clientName, authCompletionHandler);
+ return authX509(runCommand, params, clientName).onError(errorHandler);
#endif
else if (saslClientAuthenticate != nullptr)
- return saslClientAuthenticate(runCommand, hostname, params, authCompletionHandler);
+ return saslClientAuthenticate(runCommand, hostname, params).onError(errorHandler);
- return handler({ErrorCodes::AuthenticationFailed,
- mechanism + " mechanism support not compiled into client library."});
+ return Status(ErrorCodes::AuthenticationFailed,
+ mechanism + " mechanism support not compiled into client library.");
};
-void asyncAuth(RunCommandHook runCommand,
- const BSONObj& params,
- const HostAndPort& hostname,
- StringData clientName,
- AuthCompletionHandler handler) {
- auth(runCommand, params, hostname, clientName, std::move(handler));
+AuthMongoCRHandler authMongoCR = authMongoCRImpl;
+
+static stdx::mutex internalAuthKeysMutex;
+static bool internalAuthSet = false;
+static std::vector<std::string> internalAuthKeys;
+static BSONObj internalAuthParams;
+
+void setInternalAuthKeys(const std::vector<std::string>& keys) {
+ stdx::lock_guard<stdx::mutex> lk(internalAuthKeysMutex);
+
+ internalAuthKeys = keys;
+ fassert(50996, internalAuthKeys.size() > 0);
+ internalAuthSet = true;
}
-} // namespace
+void setInternalUserAuthParams(BSONObj obj) {
+ stdx::lock_guard<stdx::mutex> lk(internalAuthKeysMutex);
+ internalAuthParams = obj.getOwned();
+ internalAuthKeys.clear();
+ internalAuthSet = true;
+}
-AuthMongoCRHandler authMongoCR = authMongoCRImpl;
+bool hasMultipleInternalAuthKeys() {
+ stdx::lock_guard<stdx::mutex> lk(internalAuthKeysMutex);
+ return internalAuthSet && internalAuthKeys.size() > 1;
+}
-void authenticateClient(const BSONObj& params,
- const HostAndPort& hostname,
- StringData clientName,
- RunCommandHook runCommand,
- AuthCompletionHandler handler) {
- if (handler) {
- // Run asynchronously
- return asyncAuth(std::move(runCommand), params, hostname, clientName, std::move(handler));
- } else {
- // Run synchronously through async framework
- // NOTE: this assumes that runCommand executes synchronously.
- asyncAuth(runCommand, params, hostname, clientName, [](AuthResponse response) {
- // DBClient expects us to throw in case of an auth error.
- uassertStatusOK(response.status);
-
- auto serverResponse = response.data;
- uassert(ErrorCodes::AuthenticationFailed,
- serverResponse["errmsg"].str(),
- isOk(serverResponse));
- });
+bool isInternalAuthSet() {
+ stdx::lock_guard<stdx::mutex> lk(internalAuthKeysMutex);
+ return internalAuthSet;
+}
+
+BSONObj getInternalAuthParams(size_t idx, const std::string& mechanism) {
+ stdx::lock_guard<stdx::mutex> lk(internalAuthKeysMutex);
+ if (!internalAuthSet) {
+ return BSONObj();
+ }
+
+ // If we've set a specific BSONObj as the internal auth pararms, return it if the index
+ // is zero (there are no alternate credentials if we've set a BSONObj explicitly).
+ if (!internalAuthParams.isEmpty()) {
+ return idx == 0 ? internalAuthParams : BSONObj();
+ }
+
+ // If the index is larger than the number of keys we know about then return an empty
+ // BSONObj.
+ if (idx + 1 > internalAuthKeys.size()) {
+ return BSONObj();
}
+
+ auto password = internalAuthKeys.at(idx);
+ if (mechanism == kMechanismScramSha1) {
+ password = mongo::createPasswordDigest(
+ internalSecurity.user->getName().getUser().toString(), password);
+ }
+
+ return BSON(saslCommandMechanismFieldName << mechanism << saslCommandUserDBFieldName
+ << internalSecurity.user->getName().getDB()
+ << saslCommandUserFieldName
+ << internalSecurity.user->getName().getUser()
+ << saslCommandPasswordFieldName
+ << password
+ << saslCommandDigestPasswordFieldName
+ << false);
+}
+
+Future<std::string> negotiateSaslMechanism(RunCommandHook runCommand,
+ const UserName& username,
+ boost::optional<std::string> mechanismHint) {
+ if (mechanismHint && !mechanismHint->empty()) {
+ return Future<std::string>::makeReady(*mechanismHint);
+ }
+
+ const auto request =
+ BSON("ismaster" << 1 << "saslSupportedMechs" << username.getUnambiguousName());
+ return runCommand(OpMsgRequest::fromDBAndBody("admin"_sd, std::move(request)))
+ .then([](BSONObj reply) -> Future<std::string> {
+ auto mechsArrayObj = reply.getField("saslSupportedMechs");
+ if (mechsArrayObj.type() != Array) {
+ return Status{ErrorCodes::BadValue, "Expected array of SASL mechanism names"};
+ }
+
+ auto obj = mechsArrayObj.Obj();
+ std::vector<std::string> availableMechanisms;
+ for (const auto elem : obj) {
+ if (elem.type() != String) {
+ return Status{ErrorCodes::BadValue, "Expected array of SASL mechanism names"};
+ }
+ availableMechanisms.push_back(elem.checkAndGetStringData().toString());
+ // The drivers spec says that if SHA-256 is available then it MUST be selected
+ // as the SASL mech.
+ if (availableMechanisms.back() == kMechanismScramSha256) {
+ return availableMechanisms.back();
+ }
+ }
+
+ return availableMechanisms.empty() ? kInternalAuthFallbackMechanism.toString()
+ : availableMechanisms.front();
+ });
+}
+
+Future<void> authenticateInternalClient(const std::string& clientSubjectName,
+ boost::optional<std::string> mechanismHint,
+ RunCommandHook runCommand) {
+ return negotiateSaslMechanism(runCommand, internalSecurity.user->getName(), mechanismHint)
+ .then([runCommand, clientSubjectName](std::string mechanism) -> Future<void> {
+ auto params = getInternalAuthParams(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);
+ if (!altCreds.isEmpty()) {
+ return authenticateClient(
+ altCreds, HostAndPort(), clientSubjectName, runCommand);
+ }
+ return status;
+ });
+ });
}
BSONObj buildAuthParams(StringData dbname,