diff options
-rw-r--r-- | src/mongo/client/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/client/cyrus_sasl_client_session.cpp | 2 | ||||
-rw-r--r-- | src/mongo/client/native_sasl_client_session.cpp | 11 | ||||
-rw-r--r-- | src/mongo/client/sasl_scram_client_conversation.cpp | 16 | ||||
-rw-r--r-- | src/mongo/client/sasl_scram_client_conversation.h | 21 | ||||
-rw-r--r-- | src/mongo/db/auth/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/native_sasl_authentication_session.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_authentication_session.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_authentication_session.h | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_authentication_session_test.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_options.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_scram_server_conversation.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_scram_server_conversation.h | 15 | ||||
-rw-r--r-- | src/mongo/db/commands/authentication_commands.h | 1 |
14 files changed, 79 insertions, 20 deletions
diff --git a/src/mongo/client/SConscript b/src/mongo/client/SConscript index 6b54c9c7e70..3a48533419e 100644 --- a/src/mongo/client/SConscript +++ b/src/mongo/client/SConscript @@ -110,6 +110,7 @@ saslClientEnv.Library( '$BUILD_DIR/mongo/executor/remote_command', '$BUILD_DIR/mongo/rpc/command_status', '$BUILD_DIR/mongo/rpc/metadata', + '$BUILD_DIR/mongo/util/icu', '$BUILD_DIR/mongo/util/md5', '$BUILD_DIR/mongo/util/net/network', ], diff --git a/src/mongo/client/cyrus_sasl_client_session.cpp b/src/mongo/client/cyrus_sasl_client_session.cpp index 503a41303ac..e4d55c0bf42 100644 --- a/src/mongo/client/cyrus_sasl_client_session.cpp +++ b/src/mongo/client/cyrus_sasl_client_session.cpp @@ -41,7 +41,7 @@ namespace mongo { namespace { SaslClientSession* createCyrusSaslClientSession(const std::string& mech) { - if (mech == "SCRAM-SHA-1") { + if ((mech == "SCRAM-SHA-1") || (mech == "SCRAM-SHA-256")) { return new NativeSaslClientSession(); } return new CyrusSaslClientSession(); diff --git a/src/mongo/client/native_sasl_client_session.cpp b/src/mongo/client/native_sasl_client_session.cpp index bce0e220bc8..afa5073069b 100644 --- a/src/mongo/client/native_sasl_client_session.cpp +++ b/src/mongo/client/native_sasl_client_session.cpp @@ -48,8 +48,9 @@ MONGO_INITIALIZER(NativeSaslClientContext)(InitializerContext* context) { return Status::OK(); } -// Global cache for SCRAM-SHA-1 credentials -SCRAMSHA1ClientCache* scramsha1ClientCache = new SCRAMSHA1ClientCache; +// Global cache for SCRAM-SHA-1/256 credentials +auto* scramsha1ClientCache = new SCRAMClientCache<SHA1Block>; +auto* scramsha256ClientCache = new SCRAMClientCache<SHA256Block>; } // namespace @@ -67,7 +68,11 @@ Status NativeSaslClientSession::initialize() { if (mechanism == "PLAIN") { _saslConversation.reset(new SaslPLAINClientConversation(this)); } else if (mechanism == "SCRAM-SHA-1") { - _saslConversation.reset(new SaslSCRAMSHA1ClientConversation(this, scramsha1ClientCache)); + _saslConversation.reset( + new SaslSCRAMClientConversationImpl<SHA1Block>(this, scramsha1ClientCache)); + } else if (mechanism == "SCRAM-SHA-256") { + _saslConversation.reset( + new SaslSCRAMClientConversationImpl<SHA256Block>(this, scramsha256ClientCache)); } else { return Status(ErrorCodes::BadValue, mongoutils::str::stream() << "SASL mechanism " << mechanism diff --git a/src/mongo/client/sasl_scram_client_conversation.cpp b/src/mongo/client/sasl_scram_client_conversation.cpp index 0a95f5f9cf8..5cca962271f 100644 --- a/src/mongo/client/sasl_scram_client_conversation.cpp +++ b/src/mongo/client/sasl_scram_client_conversation.cpp @@ -94,8 +94,13 @@ StatusWith<bool> SaslSCRAMClientConversation::_firstStep(std::string* outputData binaryNonce[1] = sr->nextInt64(); binaryNonce[2] = sr->nextInt64(); - std::string user = - _saslClientSession->getParameter(SaslClientSession::parameterUser).toString(); + auto swUser = + saslPrep(_saslClientSession->getParameter(SaslClientSession::parameterUser).toString()); + if (!swUser.isOK()) { + return swUser.getStatus(); + } + auto user = swUser.getValue(); + encodeSCRAMUsername(user); _clientNonce = base64::encode(reinterpret_cast<char*>(binaryNonce), sizeof(binaryNonce)); @@ -160,16 +165,15 @@ StatusWith<bool> SaslSCRAMClientConversation::_secondStep(const std::vector<stri // Append client-final-message-without-proof to _authMessage _authMessage += "c=biws,r=" + nonce; - std::string decodedSalt; + std::string decodedSalt, clientProof; try { decodedSalt = base64::decode(salt); + clientProof = generateClientProof( + std::vector<std::uint8_t>(decodedSalt.begin(), decodedSalt.end()), iterationCount); } catch (const DBException& ex) { return StatusWith<bool>(ex.toStatus()); } - auto clientProof = generateClientProof( - std::vector<std::uint8_t>(decodedSalt.begin(), decodedSalt.end()), iterationCount); - StringBuilder sb; sb << "c=biws,r=" << nonce << ",p=" << clientProof; *outputData = sb.str(); diff --git a/src/mongo/client/sasl_scram_client_conversation.h b/src/mongo/client/sasl_scram_client_conversation.h index f714e3145d7..63ffca61db6 100644 --- a/src/mongo/client/sasl_scram_client_conversation.h +++ b/src/mongo/client/sasl_scram_client_conversation.h @@ -38,6 +38,7 @@ #include "mongo/client/sasl_client_session.h" #include "mongo/client/scram_client_cache.h" #include "mongo/crypto/mechanism_scram.h" +#include "mongo/util/icu.h" namespace mongo { @@ -70,6 +71,11 @@ public: */ virtual bool verifyServerSignature(StringData sig) const = 0; + /** + * Runs saslPrep except on SHA-1. + */ + virtual StatusWith<std::string> saslPrep(StringData val) const = 0; + private: /** * Generates client-first-message. @@ -103,10 +109,9 @@ public: std::string generateClientProof(const std::vector<std::uint8_t>& salt, size_t iterationCount) final { - scram::Presecrets<HashBlock> presecrets( - _saslClientSession->getParameter(SaslClientSession::parameterPassword).toString(), - salt, - iterationCount); + auto password = uassertStatusOK(saslPrep( + _saslClientSession->getParameter(SaslClientSession::parameterPassword).toString())); + scram::Presecrets<HashBlock> presecrets(password, salt, iterationCount); auto targetHost = HostAndPort::parse( _saslClientSession->getParameter(SaslClientSession::parameterServiceHostAndPort)); @@ -129,6 +134,14 @@ public: return _credentials.verifyServerSignature(_authMessage, sig); } + StatusWith<std::string> saslPrep(StringData val) const final { + if (std::is_same<SHA1Block, HashBlock>::value) { + return val.toString(); + } else { + return mongo::saslPrep(val); + } + } + private: // Secrets and secrets cache scram::Secrets<HashBlock> _credentials; diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index 9be76b3c20d..8752e0cec57 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -181,6 +181,7 @@ env.Library('saslauth', 'sasl_options', '$BUILD_DIR/mongo/base/secure_allocator', '$BUILD_DIR/mongo/db/commands/test_commands_enabled', + '$BUILD_DIR/mongo/util/icu', '$BUILD_DIR/mongo/util/net/network', ], ) diff --git a/src/mongo/db/auth/native_sasl_authentication_session.cpp b/src/mongo/db/auth/native_sasl_authentication_session.cpp index bfddca7fc5f..74b7688f265 100644 --- a/src/mongo/db/auth/native_sasl_authentication_session.cpp +++ b/src/mongo/db/auth/native_sasl_authentication_session.cpp @@ -80,7 +80,8 @@ MONGO_INITIALIZER_WITH_PREREQUISITES(PostSaslCommands, ("NativeSaslServerCore")) for (size_t i = 0; i < saslGlobalParams.authenticationMechanisms.size(); ++i) { const std::string& mechanism = saslGlobalParams.authenticationMechanisms[i]; - if (mechanism == "MONGODB-CR" || mechanism == "MONGODB-X509") { + if (mechanism == "SCRAM-SHA-1" || mechanism == "SCRAM-SHA-256" || + mechanism == "MONGODB-X509") { // Not a SASL mechanism; no need to smoke test built-in mechanisms. continue; } @@ -124,7 +125,9 @@ Status NativeSaslAuthenticationSession::start(StringData authenticationDatabase, if (mechanism == "PLAIN") { _saslConversation.reset(new SaslPLAINServerConversation(this)); } else if (mechanism == "SCRAM-SHA-1") { - _saslConversation.reset(new SaslSCRAMSHA1ServerConversation(this)); + _saslConversation.reset(new SaslSCRAMServerConversationImpl<SHA1Block>(this)); + } else if (mechanism == "SCRAM-SHA-256") { + _saslConversation.reset(new SaslSCRAMServerConversationImpl<SHA256Block>(this)); } else { return Status(ErrorCodes::BadValue, mongoutils::str::stream() << "SASL mechanism " << mechanism diff --git a/src/mongo/db/auth/sasl_authentication_session.cpp b/src/mongo/db/auth/sasl_authentication_session.cpp index b2679702da7..5195a6aa193 100644 --- a/src/mongo/db/auth/sasl_authentication_session.cpp +++ b/src/mongo/db/auth/sasl_authentication_session.cpp @@ -49,6 +49,7 @@ namespace mongo { SaslAuthenticationSession::SaslAuthenticationSessionFactoryFn SaslAuthenticationSession::create; // Mechanism name constants. +const char SaslAuthenticationSession::mechanismSCRAMSHA256[] = "SCRAM-SHA-256"; const char SaslAuthenticationSession::mechanismSCRAMSHA1[] = "SCRAM-SHA-1"; const char SaslAuthenticationSession::mechanismGSSAPI[] = "GSSAPI"; const char SaslAuthenticationSession::mechanismPLAIN[] = "PLAIN"; diff --git a/src/mongo/db/auth/sasl_authentication_session.h b/src/mongo/db/auth/sasl_authentication_session.h index 621ccd1fd3c..c28e60919e8 100644 --- a/src/mongo/db/auth/sasl_authentication_session.h +++ b/src/mongo/db/auth/sasl_authentication_session.h @@ -57,6 +57,7 @@ public: static SaslAuthenticationSessionFactoryFn create; // Mechanism name constants. + static const char mechanismSCRAMSHA256[]; static const char mechanismSCRAMSHA1[]; static const char mechanismGSSAPI[]; static const char mechanismPLAIN[]; diff --git a/src/mongo/db/auth/sasl_authentication_session_test.cpp b/src/mongo/db/auth/sasl_authentication_session_test.cpp index 2906d9f04c7..ebabda5bfe3 100644 --- a/src/mongo/db/auth/sasl_authentication_session_test.cpp +++ b/src/mongo/db/auth/sasl_authentication_session_test.cpp @@ -77,9 +77,17 @@ SaslConversation::SaslConversation(std::string mech) true, BSONObj())); - const auto authHash = (mech == "SCRAM-SHA-1") ? "frim" : createPasswordDigest("andy", "frim"); - const auto creds = BSON("SCRAM-SHA-1" << scram::SHA1Secrets::generateCredentials( - authHash, saslGlobalParams.scramSHA1IterationCount.load())); + // PLAIN mechanism uses the same hashed password as SCRAM-SHA-1, + // but SCRAM-SHA-1's implementation makes the assumption the hashing has already + // happened at both ends. + // SCRAM-SHA-256 doesn't have this problem and always uses a pure password as input. + const auto pwHash = (mech == "PLAIN") ? createPasswordDigest("andy", "frim") : "frim"; + const auto creds = + BSON("SCRAM-SHA-1" << scram::Secrets<SHA1Block>::generateCredentials( + pwHash, saslGlobalParams.scramSHA1IterationCount.load()) + << "SCRAM-SHA-256" + << scram::Secrets<SHA256Block>::generateCredentials( + "frim", saslGlobalParams.scramSHA256IterationCount.load())); ASSERT_OK(authManagerExternalState->insert(&opCtx, NamespaceString("admin.system.users"), @@ -213,6 +221,7 @@ void SaslConversation::testWrongServerMechanism() { DEFINE_ALL_MECHANISM_TESTS(SaslConversation##CLASS_SUFFIX) TEST_MECHANISM(SCRAMSHA1, "SCRAM-SHA-1") +TEST_MECHANISM(SCRAMSHA256, "SCRAM-SHA-256") TEST_MECHANISM(PLAIN, "PLAIN") TEST_F(SaslIllegalConversation, IllegalClientMechanism) { diff --git a/src/mongo/db/auth/sasl_options.cpp b/src/mongo/db/auth/sasl_options.cpp index 5e48aaaf1e8..f35825c6d14 100644 --- a/src/mongo/db/auth/sasl_options.cpp +++ b/src/mongo/db/auth/sasl_options.cpp @@ -56,6 +56,7 @@ SASLGlobalParams::SASLGlobalParams() { // Authentication mechanisms supported by default. authenticationMechanisms.push_back("MONGODB-X509"); authenticationMechanisms.push_back("SCRAM-SHA-1"); + authenticationMechanisms.push_back("SCRAM-SHA-256"); // Default iteration count for SCRAM authentication. scramSHA1IterationCount.store(defaultScramSHA1IterationCount); diff --git a/src/mongo/db/auth/sasl_scram_server_conversation.cpp b/src/mongo/db/auth/sasl_scram_server_conversation.cpp index 2ac64aedb34..7ae3373e614 100644 --- a/src/mongo/db/auth/sasl_scram_server_conversation.cpp +++ b/src/mongo/db/auth/sasl_scram_server_conversation.cpp @@ -144,6 +144,12 @@ StatusWith<bool> SaslSCRAMServerConversation::_firstStep(std::vector<string>& in decodeSCRAMUsername(_user); + auto swUser = saslPrep(_user); + if (!swUser.isOK()) { + return swUser.getStatus(); + } + _user = std::move(swUser.getValue()); + // SERVER-16534, SCRAM-SHA-1 must be enabled for authenticating the internal user, so that // cluster members may communicate with each other. Hence ignore disabled auth mechanism // for the internal user. diff --git a/src/mongo/db/auth/sasl_scram_server_conversation.h b/src/mongo/db/auth/sasl_scram_server_conversation.h index 4c08140ef41..d2166bb4377 100644 --- a/src/mongo/db/auth/sasl_scram_server_conversation.h +++ b/src/mongo/db/auth/sasl_scram_server_conversation.h @@ -33,9 +33,11 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" +#include "mongo/base/status_with.h" #include "mongo/base/string_data.h" #include "mongo/crypto/mechanism_scram.h" #include "mongo/db/auth/sasl_server_conversation.h" +#include "mongo/util/icu.h" namespace mongo { /** @@ -83,6 +85,11 @@ public: */ virtual std::string generateServerSignature() const = 0; + /** + * Runs saslPrep except on SHA-1. + */ + virtual StatusWith<std::string> saslPrep(StringData str) const = 0; + private: /** * Parse client-first-message and generate server-first-message @@ -138,6 +145,14 @@ public: return _credentials.generateServerSignature(_authMessage); } + StatusWith<std::string> saslPrep(StringData str) const final { + if (std::is_same<SHA1Block, HashBlock>::value) { + return str.toString(); + } else { + return mongo::saslPrep(str); + } + } + private: scram::Secrets<HashBlock> _credentials; }; diff --git a/src/mongo/db/commands/authentication_commands.h b/src/mongo/db/commands/authentication_commands.h index c79dc4a6979..e04237ea91d 100644 --- a/src/mongo/db/commands/authentication_commands.h +++ b/src/mongo/db/commands/authentication_commands.h @@ -76,7 +76,6 @@ private: const std::string& mechanism, const UserName& user, const BSONObj& cmdObj); - Status _authenticateCR(OperationContext* opCtx, const UserName& user, const BSONObj& cmdObj); Status _authenticateX509(OperationContext* opCtx, const UserName& user, const BSONObj& cmdObj); }; |