summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndreas Nilsson <andreas.nilsson@10gen.com>2014-09-12 11:29:50 -0400
committerAndreas Nilsson <andreas.nilsson@10gen.com>2014-09-17 15:44:49 -0700
commitdedf038860746ad0bed3a6f37ae702587933b5f8 (patch)
tree4c7939436e56c4953ae71d55ccd4d887015cf9d6 /src
parent859876746f3abcf7a97e150e1179cc70325ea274 (diff)
downloadmongo-dedf038860746ad0bed3a6f37ae702587933b5f8.tar.gz
SERVER-15179 Auth upgrade from MONGODB-CR -> SCRAM-SHA-1
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp112
-rw-r--r--src/mongo/db/auth/authorization_manager.h11
-rw-r--r--src/mongo/db/auth/native_sasl_authentication_session.cpp6
-rw-r--r--src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp12
-rw-r--r--src/mongo/db/commands/auth_schema_upgrade_d.cpp132
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp56
-rw-r--r--src/mongo/db/db.cpp2
-rw-r--r--src/mongo/s/commands/auth_schema_upgrade_s.cpp2
8 files changed, 243 insertions, 90 deletions
diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp
index 6946a27bfc3..1d1dca82a71 100644
--- a/src/mongo/db/auth/authorization_manager.cpp
+++ b/src/mongo/db/auth/authorization_manager.cpp
@@ -30,6 +30,7 @@
#include "mongo/db/auth/authorization_manager.h"
+#include <boost/bind.hpp>
#include <boost/thread/mutex.hpp>
#include <memory>
#include <string>
@@ -44,6 +45,7 @@
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/authz_documents_update_guard.h"
#include "mongo/db/auth/authz_manager_external_state.h"
+#include "mongo/db/auth/mechanism_scram.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/role_graph.h"
#include "mongo/db/auth/user.h"
@@ -721,6 +723,106 @@ namespace mongo {
return _externalState->releaseAuthzUpdateLock();
}
+namespace {
+
+ /**
+ * Logs that the auth schema upgrade failed because of "status" and returns "status".
+ */
+ Status logUpgradeFailed(const Status& status) {
+ log() << "Auth schema upgrade failed with " << status;
+ return status;
+ }
+
+ /**
+ * Updates a single user document from MONGODB-CR to SCRAM credentials.
+ *
+ * Throws a DBException on errors.
+ */
+ void updateUserCredentials(OperationContext* txn,
+ AuthzManagerExternalState* externalState,
+ const StringData& sourceDB,
+ const BSONObj& userDoc,
+ const BSONObj& writeConcern) {
+ BSONElement credentialsElement = userDoc["credentials"];
+ uassert(18743,
+ mongoutils::str::stream() << "While preparing to upgrade user doc from "
+ "2.6/2.8 user data schema to the 2.8 SCRAM only schema, found a user doc "
+ "with missing or incorrectly formatted credentials: "
+ << userDoc.toString(),
+ credentialsElement.type() == Object);
+
+ BSONObj credentialsObj = credentialsElement.Obj();
+ BSONElement mongoCRElement = credentialsObj["MONGODB-CR"];
+ BSONElement scramElement = credentialsObj["SCRAM-SHA-1"];
+
+ // Ignore any user documents that already have SCRAM credentials. This should only
+ // occur if a previous authSchemaUpgrade was interrupted halfway.
+ if (!scramElement.eoo()) {
+ return;
+ }
+
+ uassert(18744,
+ mongoutils::str::stream() << "While preparing to upgrade user doc from "
+ "2.6/2.8 user data schema to the 2.8 SCRAM only schema, found a user doc "
+ "missing MONGODB-CR credentials :"
+ << userDoc.toString(),
+ !mongoCRElement.eoo());
+
+ std::string hashedPassword = mongoCRElement.String();
+
+ BSONObj query = BSON("_id" << userDoc["_id"].String());
+ BSONObjBuilder updateBuilder;
+ {
+ BSONObjBuilder toSetBuilder(updateBuilder.subobjStart("$set"));
+ toSetBuilder << "credentials" <<
+ BSON("SCRAM-SHA-1" << scram::generateCredentials(hashedPassword));
+ }
+
+ uassertStatusOK(externalState->updateOne(txn,
+ NamespaceString("admin", "system.users"),
+ query,
+ updateBuilder.obj(),
+ true,
+ writeConcern));
+ }
+
+ /** Loop through all the user documents in the admin.system.users collection.
+ * For each user document:
+ * 1. Compute SCRAM credentials based on the MONGODB-CR hash
+ * 2. Remove the MONGODB-CR hash
+ * 3. Add SCRAM credentials to the user document credentials section
+ */
+ Status updateCredentials(
+ OperationContext* txn,
+ AuthzManagerExternalState* externalState,
+ const BSONObj& writeConcern) {
+
+ // Loop through and update the user documents in admin.system.users.
+ Status status = externalState->query(
+ txn,
+ NamespaceString("admin", "system.users"),
+ BSONObj(),
+ BSONObj(),
+ boost::bind(updateUserCredentials, txn, externalState, "admin", _1, writeConcern));
+ if (!status.isOK())
+ return logUpgradeFailed(status);
+
+ // Update the schema version document.
+ status = externalState->updateOne(
+ txn,
+ AuthorizationManager::versionCollectionNamespace,
+ AuthorizationManager::versionDocumentQuery,
+ BSON("$set" << BSON(AuthorizationManager::schemaVersionFieldName <<
+ AuthorizationManager::schemaVersion28SCRAM)),
+ true,
+ writeConcern);
+ if (!status.isOK())
+ return logUpgradeFailed(status);
+
+ return Status::OK();
+ }
+} //namespace
+
Status AuthorizationManager::upgradeSchemaStep(
OperationContext* txn, const BSONObj& writeConcern, bool* isDone) {
int authzVersion;
@@ -728,9 +830,15 @@ namespace mongo {
if (!status.isOK()) {
return status;
}
-
+
switch (authzVersion) {
- case schemaVersion26Final:
+ case schemaVersion26Final: {
+ Status status = updateCredentials(txn, _externalState.get(), writeConcern);
+ if (status.isOK())
+ *isDone = true;
+ return status;
+ }
+ case schemaVersion28SCRAM:
*isDone = true;
return Status::OK();
default:
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index b45712847d0..600a2ef032a 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -121,11 +121,18 @@ namespace mongo {
static const int schemaVersion26Upgrade = 2;
/**
- * Auth schema version for MongoDB 2.6. Users are stored in admin.system.users,
- * roles in admin.system.roles.
+ * Auth schema version for MongoDB 2.6 and 2.8 MONGODB-CR/SCRAM mixed auth mode.
+ * Users are stored in admin.system.users, roles in admin.system.roles.
*/
static const int schemaVersion26Final = 3;
+ /**
+ * Auth schema version for MongoDB 2.8 SCRAM only mode.
+ * Users are stored in admin.system.users, roles in admin.system.roles.
+ * MONGODB-CR credentials have been replaced with SCRAM credentials in the user documents.
+ */
+ static const int schemaVersion28SCRAM = 4;
+
// TODO: Make the following functions no longer static.
/**
diff --git a/src/mongo/db/auth/native_sasl_authentication_session.cpp b/src/mongo/db/auth/native_sasl_authentication_session.cpp
index b9b62d70c3f..96a7ff16c1b 100644
--- a/src/mongo/db/auth/native_sasl_authentication_session.cpp
+++ b/src/mongo/db/auth/native_sasl_authentication_session.cpp
@@ -143,6 +143,12 @@ namespace {
Status NativeSaslAuthenticationSession::step(const StringData& inputData,
std::string* outputData) {
+ if (!_saslConversation) {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() <<
+ "The authentication session has not been properly initialized");
+ }
+
StatusWith<bool> status = _saslConversation->step(inputData, outputData);
if (status.isOK()) {
_done = status.getValue();
diff --git a/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp b/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
index cddab8001b7..373093a26fc 100644
--- a/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
+++ b/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
@@ -41,6 +41,7 @@
#include "mongo/db/auth/mechanism_scram.h"
#include "mongo/platform/random.h"
#include "mongo/util/base64.h"
+#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/password_digest.h"
#include "mongo/util/text.h"
@@ -162,7 +163,16 @@ namespace mongo {
_creds = userObj->getCredentials();
_saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
releaseUser(userObj);
-
+
+ // Generate SCRAM credentials on the fly for mixed MONGODB-CR/SCRAM mode.
+ if (_creds.scram.salt.empty() && !_creds.password.empty()) {
+ BSONObj scramCreds = scram::generateCredentials(_creds.password);
+ _creds.scram.iterationCount = scramCreds["iterationCount"].Int();
+ _creds.scram.salt = scramCreds["salt"].String();
+ _creds.scram.storedKey = scramCreds["storedKey"].String();
+ _creds.scram.serverKey = scramCreds["serverKey"].String();
+ }
+
// Generate server-first-message
// Create text-based nonce as base64 encoding of a binary blob of length multiple of 3
const int nonceLenQWords = 3;
diff --git a/src/mongo/db/commands/auth_schema_upgrade_d.cpp b/src/mongo/db/commands/auth_schema_upgrade_d.cpp
index 0de0cb58e72..c4b3b73bad8 100644
--- a/src/mongo/db/commands/auth_schema_upgrade_d.cpp
+++ b/src/mongo/db/commands/auth_schema_upgrade_d.cpp
@@ -46,73 +46,71 @@ namespace {
Status checkReplicaMemberVersions() {
- // TODO(spencer): SERVER-14460 Resurrect or remove this function.
-// if (repl::getGlobalReplicationCoordinator()->getReplicationMode() !=
-// repl::ReplicationCoordinator::modeReplSet)
-// return Status::OK();
-//
-//
-// std::list<repl::Target> rsMembers;
-// try {
-// const unsigned rsSelfId = repl::theReplSet->selfId();
-// const std::vector<repl::ReplSetConfig::MemberCfg>& rsMemberConfigs =
-// repl::theReplSet->config().members;
-// for (size_t i = 0; i < rsMemberConfigs.size(); ++i) {
-// const unsigned otherId = rsMemberConfigs[i]._id;
-// if (rsSelfId == otherId)
-// continue;
-// const repl::Member* other = repl::theReplSet->findById(otherId);
-// if (!other) {
-// log() << "During authSchemaUpgrade, no information about replica set member "
-// "with id " << otherId << "; ignoring.";
-// continue;
-// }
-// if (!other->hbinfo().maybeUp()) {
-// log() << "During authSchemaUpgrade, replica set member " << other->h() <<
-// " is down; ignoring.";
-// continue;
-// }
-// rsMembers.push_back(repl::Target(other->fullName()));
-// }
-//
-// multiCommand(BSON("buildInfo" << 1), rsMembers);
-// }
-// catch (const DBException& ex) {
-// return ex.toStatus();
-// }
-//
-// for (std::list<repl::Target>::const_iterator iter = rsMembers.begin();
-// iter != rsMembers.end();
-// ++iter) {
-//
-// if (!iter->ok) {
-// logger::LogstreamBuilder wlog = warning();
-// wlog << "During authSchemaUpgrade, could not run buildInfo command on " <<
-// iter->toHost;
-// if (!iter->result.isEmpty())
-// wlog << "; response was " << iter->result.toString();
-// wlog << "; ignoring.";
-// continue;
-// }
-//
-// const char* version = iter->result["version"].valuestrsafe();
-// if (!*version) {
-// return Status(ErrorCodes::RemoteValidationError, mongoutils::str::stream() <<
-// "Missing or non-string \"version\" field in result of buildInfo "
-// "command sent to " << iter->toHost << "; found " <<
-// iter->result["version"]);
-// }
-//
-// if (!isSameMajorVersion(version)) {
-// BSONArray foundVersionArray = toVersionArray(version);
-// return Status(ErrorCodes::RemoteValidationError, mongoutils::str::stream() <<
-// "To upgrade auth schema in a replica set, all members must be "
-// "running the same release series of mongod; found " <<
-// foundVersionArray["0"] << '.' << foundVersionArray["1"] <<
-// " on host " << iter->toHost << " but expected " <<
-// versionArray["0"] << '.' << versionArray["1"]);
-// }
-// }
+ if (repl::getGlobalReplicationCoordinator()->getReplicationMode() !=
+ repl::ReplicationCoordinator::modeReplSet)
+ return Status::OK();
+
+ std::list<repl::Target> rsMembers;
+ try {
+ const unsigned rsSelfId = repl::theReplSet->selfId();
+ const std::vector<repl::ReplSetConfig::MemberCfg>& rsMemberConfigs =
+ repl::theReplSet->config().members;
+ for (size_t i = 0; i < rsMemberConfigs.size(); ++i) {
+ const unsigned otherId = rsMemberConfigs[i]._id;
+ if (rsSelfId == otherId)
+ continue;
+ const repl::Member* other = repl::theReplSet->findById(otherId);
+ if (!other) {
+ log() << "During authSchemaUpgrade, no information about replica set member "
+ "with id " << otherId << "; ignoring.";
+ continue;
+ }
+ if (!other->hbinfo().maybeUp()) {
+ log() << "During authSchemaUpgrade, replica set member " << other->h() <<
+ " is down; ignoring.";
+ continue;
+ }
+ rsMembers.push_back(repl::Target(other->fullName()));
+ }
+
+ multiCommand(BSON("buildInfo" << 1), rsMembers);
+ }
+ catch (const DBException& ex) {
+ return ex.toStatus();
+ }
+
+ for (std::list<repl::Target>::const_iterator iter = rsMembers.begin();
+ iter != rsMembers.end();
+ ++iter) {
+
+ if (!iter->ok) {
+ logger::LogstreamBuilder wlog = warning();
+ wlog << "During authSchemaUpgrade, could not run buildInfo command on " <<
+ iter->toHost;
+ if (!iter->result.isEmpty())
+ wlog << "; response was " << iter->result.toString();
+ wlog << "; ignoring.";
+ continue;
+ }
+
+ const char* version = iter->result["version"].valuestrsafe();
+ if (!*version) {
+ return Status(ErrorCodes::RemoteValidationError, mongoutils::str::stream() <<
+ "Missing or non-string \"version\" field in result of buildInfo "
+ "command sent to " << iter->toHost << "; found " <<
+ iter->result["version"]);
+ }
+
+ if (!isSameMajorVersion(version)) {
+ BSONArray foundVersionArray = toVersionArray(version);
+ return Status(ErrorCodes::RemoteValidationError, mongoutils::str::stream() <<
+ "To upgrade auth schema in a replica set, all members must be "
+ "running the same release series of mongod; found " <<
+ foundVersionArray["0"] << '.' << foundVersionArray["1"] <<
+ " on host " << iter->toHost << " but expected " <<
+ versionArray["0"] << '.' << versionArray["1"]);
+ }
+ }
return Status(ErrorCodes::InternalError,
"Auth schema upgrade check for replica sets not implemented");
}
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index 0233adc067d..f4970409bd5 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -278,11 +278,11 @@ namespace mongo {
return status;
}
- if (foundSchemaVersion != AuthorizationManager::schemaVersion26Final) {
+ if (foundSchemaVersion < AuthorizationManager::schemaVersion26Final) {
return Status(
ErrorCodes::AuthSchemaIncompatible,
str::stream() << "User and role management commands require auth data to have "
- "schema version " << AuthorizationManager::schemaVersion26Final <<
+ "at least schema version " << AuthorizationManager::schemaVersion26Final <<
" but found " << foundSchemaVersion);
}
return authzManager->writeAuthSchemaVersionIfNeeded(txn);
@@ -296,13 +296,12 @@ namespace mongo {
return status;
}
- if (foundSchemaVersion != AuthorizationManager::schemaVersion26Final &&
- foundSchemaVersion != AuthorizationManager::schemaVersion26Upgrade) {
+ if (foundSchemaVersion < AuthorizationManager::schemaVersion26Upgrade) {
return Status(
ErrorCodes::AuthSchemaIncompatible,
str::stream() << "The usersInfo and rolesInfo commands require auth data to "
- "have schema version " << AuthorizationManager::schemaVersion26Final <<
- " or " << AuthorizationManager::schemaVersion26Upgrade <<
+ "have at least schema version " <<
+ AuthorizationManager::schemaVersion26Upgrade <<
" but found " << foundSchemaVersion);
}
return Status::OK();
@@ -427,14 +426,26 @@ namespace mongo {
args.mechanism.empty()) {
// At the moment we are ignoring the mechanism parameter and create
- // both SCRAM-SHA-1 and MONGODB-CR credentials for all new users
+ // both SCRAM-SHA-1 and MONGODB-CR credentials for all new users.
BSONObjBuilder credentialsBuilder(userObjBuilder.subobjStart("credentials"));
- BSONObj scramCred = scram::generateCredentials(args.hashedPassword);
-
- if(!scramCred.isEmpty()) {
- credentialsBuilder.append("SCRAM-SHA-1",scramCred);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ int authzVersion;
+ Status status = authzManager->getAuthorizationVersion(txn, &authzVersion);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
+ }
+
+ // Add SCRAM credentials for appropriate authSchemaVersions.
+ if (authzVersion > AuthorizationManager::schemaVersion26Final) {
+ BSONObj scramCred = scram::generateCredentials(args.hashedPassword);
+ if(!scramCred.isEmpty()) {
+ credentialsBuilder.append("SCRAM-SHA-1", scramCred);
+ }
+ }
+ else { // Otherwise default to MONGODB-CR.
+ credentialsBuilder.append("MONGODB-CR", args.hashedPassword);
}
- credentialsBuilder.append("MONGODB-CR", args.hashedPassword);
credentialsBuilder.done();
}
else {
@@ -594,11 +605,24 @@ namespace mongo {
if (args.hasHashedPassword) {
// Create both SCRAM-SHA-1 and MONGODB-CR credentials for all new users
BSONObjBuilder credentialsBuilder(updateSetBuilder.subobjStart("credentials"));
- BSONObj scramCred = scram::generateCredentials(args.hashedPassword);
- if(!scramCred.isEmpty()) {
- credentialsBuilder.append("SCRAM-SHA-1",scramCred);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ int authzVersion;
+ Status status = authzManager->getAuthorizationVersion(txn, &authzVersion);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
+ }
+
+ // Add SCRAM credentials for appropriate authSchemaVersions
+ if (authzVersion > AuthorizationManager::schemaVersion26Final) {
+ BSONObj scramCred = scram::generateCredentials(args.hashedPassword);
+ if(!scramCred.isEmpty()) {
+ credentialsBuilder.append("SCRAM-SHA-1",scramCred);
+ }
+ }
+ else { // Otherwise default to MONGODB-CR
+ credentialsBuilder.append("MONGODB-CR", args.hashedPassword);
}
- credentialsBuilder.append("MONGODB-CR", args.hashedPassword);
credentialsBuilder.done();
}
if (args.hasCustomData) {
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index aeea816ba85..2152f8c6e01 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -660,7 +660,7 @@ namespace mongo {
<< endl;
exitCleanly(EXIT_NEED_UPGRADE);
}
- if (foundSchemaVersion != AuthorizationManager::schemaVersion26Final) {
+ if (foundSchemaVersion < AuthorizationManager::schemaVersion26Final) {
log() << "Auth schema version is incompatible: "
<< "User and role management commands require auth data to have "
<< "schema version " << AuthorizationManager::schemaVersion26Final
diff --git a/src/mongo/s/commands/auth_schema_upgrade_s.cpp b/src/mongo/s/commands/auth_schema_upgrade_s.cpp
index 747624a125a..3a9a97af747 100644
--- a/src/mongo/s/commands/auth_schema_upgrade_s.cpp
+++ b/src/mongo/s/commands/auth_schema_upgrade_s.cpp
@@ -152,7 +152,7 @@ namespace {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- status = checkClusterMongoVersions(configServer.getConnectionString(), "2.5.4");
+ status = checkClusterMongoVersions(configServer.getConnectionString(), "2.7.6");
if (!status.isOK()) {
log() << "Auth schema upgrade failed: " << status << endl;
return appendCommandStatus(result, status);