summaryrefslogtreecommitdiff
path: root/src/mongo/db/auth
diff options
context:
space:
mode:
authorAndreas <agralius@gmail.com>2014-08-13 12:10:38 -0400
committerAndreas <agralius@gmail.com>2014-08-28 13:20:25 -0400
commite6a8e256ca73e7596d9f8b7b3a3b00d8c08f6554 (patch)
treecc5ca0887015b6c8d09bdc932756faaac8df3c24 /src/mongo/db/auth
parentca6a26760c2150736562f49201fcb7b4de5e53f1 (diff)
downloadmongo-e6a8e256ca73e7596d9f8b7b3a3b00d8c08f6554.tar.gz
SERVER-7596 Native SCRAM-SHA-1 server side support
Diffstat (limited to 'src/mongo/db/auth')
-rw-r--r--src/mongo/db/auth/SConscript14
-rw-r--r--src/mongo/db/auth/mechanism_scram.cpp70
-rw-r--r--src/mongo/db/auth/native_sasl_authentication_session.cpp160
-rw-r--r--src/mongo/db/auth/native_sasl_authentication_session.h70
-rw-r--r--src/mongo/db/auth/sasl_authentication_session.cpp96
-rw-r--r--src/mongo/db/auth/sasl_authentication_session.h167
-rw-r--r--src/mongo/db/auth/sasl_commands.cpp362
-rw-r--r--src/mongo/db/auth/sasl_conversation.cpp43
-rw-r--r--src/mongo/db/auth/sasl_conversation.h89
-rw-r--r--src/mongo/db/auth/sasl_options.cpp165
-rw-r--r--src/mongo/db/auth/sasl_options.h61
-rw-r--r--src/mongo/db/auth/sasl_plain_server_conversation.cpp86
-rw-r--r--src/mongo/db/auth/sasl_plain_server_conversation.h57
-rw-r--r--src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp304
-rw-r--r--src/mongo/db/auth/sasl_scramsha1_server_conversation.h82
15 files changed, 1789 insertions, 37 deletions
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript
index 62664ad8ced..fd83b9d4adb 100644
--- a/src/mongo/db/auth/SConscript
+++ b/src/mongo/db/auth/SConscript
@@ -40,9 +40,19 @@ env.Library('authcore', ['action_set.cpp',
env.Library('authservercommon',
['authorization_manager_global.cpp',
'authz_session_external_state_server_common.cpp',
- 'mechanism_scram.cpp',
+ 'sasl_commands.cpp',
'security_key.cpp'],
- LIBDEPS=['authcore'])
+ LIBDEPS=['authcore', 'authmocks', 'saslauth'])
+
+env.Library('saslauth',
+ ['mechanism_scram.cpp',
+ 'native_sasl_authentication_session.cpp',
+ 'sasl_authentication_session.cpp',
+ 'sasl_conversation.cpp',
+ 'sasl_options.cpp',
+ 'sasl_plain_server_conversation.cpp',
+ 'sasl_scramsha1_server_conversation.cpp'],
+ LIBDEPS=['authcore'])
env.Library('authmongod',
['authz_manager_external_state_d.cpp',
diff --git a/src/mongo/db/auth/mechanism_scram.cpp b/src/mongo/db/auth/mechanism_scram.cpp
index d8fb309d78c..728dd1b1899 100644
--- a/src/mongo/db/auth/mechanism_scram.cpp
+++ b/src/mongo/db/auth/mechanism_scram.cpp
@@ -69,24 +69,24 @@ const int scramHashSize = 20;
// U1 = HMAC(input, salt || 1)
fassert(17494, HMAC(EVP_sha1(),
- input,
- inputLen,
- startKey,
- saltLen + 4,
- output,
- &hashLen));
+ input,
+ inputLen,
+ startKey,
+ saltLen + 4,
+ output,
+ &hashLen));
memcpy(tmpRes, output, scramHashSize);
// tmpRes contain Uj and result contains the accumulated XOR:ed result
for (size_t i = 2; i <= iterationCount; i++) {
fassert(17495, HMAC(EVP_sha1(),
- input,
- inputLen,
- tmpRes,
- scramHashSize,
- tmpRes,
- &hashLen));
+ input,
+ inputLen,
+ tmpRes,
+ scramHashSize,
+ tmpRes,
+ &hashLen));
for (int k = 0; k < scramHashSize; k++) {
output[k] ^= tmpRes[k];
@@ -97,11 +97,11 @@ const int scramHashSize = 20;
/* Compute the SCRAM secrets storedKey and serverKey
* as defined in RFC5802 */
static void computeSCRAMProperties(const std::string& password,
- const unsigned char salt[],
- size_t saltLen,
- size_t iterationCount,
- unsigned char storedKey[scramHashSize],
- unsigned char serverKey[scramHashSize]) {
+ const unsigned char salt[],
+ size_t saltLen,
+ size_t iterationCount,
+ unsigned char storedKey[scramHashSize],
+ unsigned char serverKey[scramHashSize]) {
unsigned char saltedPassword[scramHashSize];
unsigned char clientKey[scramHashSize];
@@ -118,12 +118,12 @@ const int scramHashSize = 20;
// clientKey = HMAC(saltedPassword, "Client Key")
const std::string clientKeyConst = "Client Key";
fassert(17498, HMAC(EVP_sha1(),
- saltedPassword,
- scramHashSize,
- reinterpret_cast<const unsigned char*>(clientKeyConst.data()),
- clientKeyConst.size(),
- clientKey,
- &hashLen));
+ saltedPassword,
+ scramHashSize,
+ reinterpret_cast<const unsigned char*>(clientKeyConst.data()),
+ clientKeyConst.size(),
+ clientKey,
+ &hashLen));
// storedKey = H(clientKey)
fassert(17499, SHA1(clientKey, scramHashSize, storedKey));
@@ -131,12 +131,12 @@ const int scramHashSize = 20;
// serverKey = HMAC(saltedPassword, "Server Key")
const std::string serverKeyConst = "Server Key";
fassert(17500, HMAC(EVP_sha1(),
- saltedPassword,
- scramHashSize,
- reinterpret_cast<const unsigned char*>(serverKeyConst.data()),
- serverKeyConst.size(),
- serverKey,
- &hashLen));
+ saltedPassword,
+ scramHashSize,
+ reinterpret_cast<const unsigned char*>(serverKeyConst.data()),
+ serverKeyConst.size(),
+ serverKey,
+ &hashLen));
}
#endif //MONGO_SSL
@@ -158,7 +158,7 @@ const int scramHashSize = 20;
userSalt[0] = sr->nextInt64();
userSalt[1] = sr->nextInt64();
std::string encodedUserSalt =
- base64::encode(reinterpret_cast<char*>(&userSalt[0]), sizeof(userSalt));
+ base64::encode(reinterpret_cast<char*>(userSalt), sizeof(userSalt));
// Compute SCRAM secrets serverKey and storedKey
unsigned char storedKey[scramHashSize];
@@ -172,14 +172,14 @@ const int scramHashSize = 20;
serverKey);
std::string encodedStoredKey =
- base64::encode(reinterpret_cast<char*>(&storedKey[0]), scramHashSize);
+ base64::encode(reinterpret_cast<char*>(storedKey), scramHashSize);
std::string encodedServerKey =
- base64::encode(reinterpret_cast<char*>(&serverKey[0]), scramHashSize);
+ base64::encode(reinterpret_cast<char*>(serverKey), scramHashSize);
return BSON("iterationCount" << iterationCount <<
- "salt" << encodedUserSalt <<
- "storedKey" << encodedStoredKey <<
- "serverKey" << encodedServerKey);
+ "salt" << encodedUserSalt <<
+ "storedKey" << encodedStoredKey <<
+ "serverKey" << encodedServerKey);
#endif
}
} // namespace mongo
diff --git a/src/mongo/db/auth/native_sasl_authentication_session.cpp b/src/mongo/db/auth/native_sasl_authentication_session.cpp
new file mode 100644
index 00000000000..4c4189d17a6
--- /dev/null
+++ b/src/mongo/db/auth/native_sasl_authentication_session.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2014 MongoDB Inc. All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl
+
+#include "mongo/db/auth/native_sasl_authentication_session.h"
+
+#include <boost/range/size.hpp>
+
+#include "mongo/base/init.h"
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/bson/util/bson_extract.h"
+#include "mongo/client/sasl_client_authenticate.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/auth/authorization_manager.h"
+#include "mongo/db/auth/authorization_manager_global.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/auth/authz_manager_external_state_mock.h"
+#include "mongo/db/auth/authz_session_external_state_mock.h"
+#include "mongo/db/auth/sasl_options.h"
+#include "mongo/db/auth/sasl_plain_server_conversation.h"
+#include "mongo/db/auth/sasl_scramsha1_server_conversation.h"
+#include "mongo/db/operation_context_noop.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace {
+ SaslAuthenticationSession* createNativeSaslAuthenticationSession(
+ AuthorizationSession* authzSession) {
+ return new NativeSaslAuthenticationSession(authzSession);
+ }
+
+ MONGO_INITIALIZER(NativeSaslServerCore)(InitializerContext* context) {
+ if (saslGlobalParams.hostName.empty())
+ saslGlobalParams.hostName = getHostNameCached();
+ if (saslGlobalParams.serviceName.empty())
+ saslGlobalParams.serviceName = "mongodb";
+
+ SaslAuthenticationSession::create = createNativeSaslAuthenticationSession;
+ return Status::OK();
+ }
+
+ // PostSaslCommands is reversely dependent on CyrusSaslCommands having been run
+ MONGO_INITIALIZER_WITH_PREREQUISITES(PostSaslCommands,
+ ("NativeSaslServerCore"))
+ (InitializerContext*) {
+
+ AuthorizationManager authzManager(new AuthzManagerExternalStateMock());
+ AuthorizationSession authzSession(new AuthzSessionExternalStateMock(&authzManager));
+
+ for (size_t i = 0; i < saslGlobalParams.authenticationMechanisms.size(); ++i) {
+ const std::string& mechanism = saslGlobalParams.authenticationMechanisms[i];
+ if (mechanism == "MONGODB-CR" || mechanism == "MONGODB-X509") {
+ // Not a SASL mechanism; no need to smoke test built-in mechanisms.
+ continue;
+ }
+ scoped_ptr<SaslAuthenticationSession>
+ session(SaslAuthenticationSession::create(&authzSession));
+ Status status = session->start("test",
+ mechanism,
+ saslGlobalParams.serviceName,
+ saslGlobalParams.hostName,
+ 1,
+ true);
+ if (!status.isOK())
+ return status;
+ }
+
+ return Status::OK();
+ }
+} //namespace
+
+ NativeSaslAuthenticationSession::NativeSaslAuthenticationSession(
+ AuthorizationSession* authzSession) :
+ SaslAuthenticationSession(authzSession),
+ _mechanism("") {
+ }
+
+ NativeSaslAuthenticationSession::~NativeSaslAuthenticationSession() {}
+
+ Status NativeSaslAuthenticationSession::start(const StringData& authenticationDatabase,
+ const StringData& mechanism,
+ const StringData& serviceName,
+ const StringData& serviceHostname,
+ int64_t conversationId,
+ bool autoAuthorize) {
+ fassert(18626, conversationId > 0);
+
+ if (_conversationId != 0) {
+ return Status(ErrorCodes::AlreadyInitialized,
+ "Cannot call start() twice on same NativeSaslAuthenticationSession.");
+ }
+
+ _authenticationDatabase = authenticationDatabase.toString();
+ _mechanism = mechanism.toString();
+ _serviceName = serviceName.toString();
+ _serviceHostname = serviceHostname.toString();
+ _conversationId = conversationId;
+ _autoAuthorize = autoAuthorize;
+
+ if (mechanism == "PLAIN") {
+ _saslConversation.reset(new SaslPLAINServerConversation(this));
+ }
+ else if (mechanism == "SCRAM-SHA-1") {
+ _saslConversation.reset(new SaslSCRAMSHA1ServerConversation(this));
+ }
+ else {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "SASL mechanism " << mechanism <<
+ "is not supported");
+ }
+
+ return Status::OK();
+ }
+
+ Status NativeSaslAuthenticationSession::step(const StringData& inputData,
+ std::string* outputData) {
+ StatusWith<bool> status = _saslConversation->step(inputData, outputData);
+ if (status.isOK()) {
+ _done = status.getValue();
+ }
+ return status.getStatus();
+ }
+
+ std::string NativeSaslAuthenticationSession::getPrincipalId() const {
+ return _saslConversation->getPrincipalId();
+ }
+
+ const char* NativeSaslAuthenticationSession::getMechanism() const {
+ return _mechanism.c_str();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/native_sasl_authentication_session.h b/src/mongo/db/auth/native_sasl_authentication_session.h
new file mode 100644
index 00000000000..104e84445f2
--- /dev/null
+++ b/src/mongo/db/auth/native_sasl_authentication_session.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 10gen, Inc. All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/db/auth/authentication_session.h"
+#include "mongo/platform/cstdint.h"
+#include "mongo/db/auth/sasl_authentication_session.h"
+#include "mongo/db/auth/sasl_conversation.h"
+
+namespace mongo {
+
+ /**
+ * Authentication session data for the server side of SASL authentication.
+ */
+ class NativeSaslAuthenticationSession : public SaslAuthenticationSession {
+ MONGO_DISALLOW_COPYING(NativeSaslAuthenticationSession);
+ public:
+
+ explicit NativeSaslAuthenticationSession(AuthorizationSession* authSession);
+ virtual ~NativeSaslAuthenticationSession();
+
+ virtual Status start(const StringData& authenticationDatabase,
+ const StringData& mechanism,
+ const StringData& serviceName,
+ const StringData& serviceHostname,
+ int64_t conversationId,
+ bool autoAuthorize);
+
+ virtual Status step(const StringData& inputData, std::string* outputData);
+
+ virtual std::string getPrincipalId() const;
+
+ virtual const char* getMechanism() const;
+
+ private:
+ std::string _mechanism;
+ boost::scoped_ptr<SaslConversation> _saslConversation;
+ };
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_authentication_session.cpp b/src/mongo/db/auth/sasl_authentication_session.cpp
new file mode 100644
index 00000000000..57d842999ec
--- /dev/null
+++ b/src/mongo/db/auth/sasl_authentication_session.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 10gen, Inc. All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl
+
+#include "mongo/db/auth/sasl_authentication_session.h"
+
+#include <boost/range/size.hpp>
+
+#include "mongo/base/init.h"
+#include "mongo/base/string_data.h"
+#include "mongo/bson/util/bson_extract.h"
+#include "mongo/client/sasl_client_authenticate.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/auth/authorization_manager.h"
+#include "mongo/db/auth/authorization_manager_global.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/auth/authz_manager_external_state_mock.h"
+#include "mongo/db/auth/authz_session_external_state_mock.h"
+#include "mongo/db/operation_context_noop.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+ SaslAuthenticationSession::SaslSessionFactoryFn SaslAuthenticationSession::create = NULL;
+
+ // Mechanism name constants.
+ const char SaslAuthenticationSession::mechanismCRAMMD5[] = "CRAM-MD5";
+ const char SaslAuthenticationSession::mechanismDIGESTMD5[] = "DIGEST-MD5";
+ const char SaslAuthenticationSession::mechanismSCRAMSHA1[] = "SCRAM-SHA-1";
+ const char SaslAuthenticationSession::mechanismGSSAPI[] = "GSSAPI";
+ const char SaslAuthenticationSession::mechanismPLAIN[] = "PLAIN";
+
+ /**
+ * Standard method in mongodb for determining if "authenticatedUser" may act as "requestedUser."
+ *
+ * The standard rule in MongoDB is simple. The authenticated user name must be the same as the
+ * requested user name.
+ */
+ bool isAuthorizedCommon(SaslAuthenticationSession* session,
+ const StringData& requestedUser,
+ const StringData& authenticatedUser) {
+
+ return requestedUser == authenticatedUser;
+ }
+
+ SaslAuthenticationSession::SaslAuthenticationSession(AuthorizationSession* authzSession) :
+ AuthenticationSession(AuthenticationSession::SESSION_TYPE_SASL),
+ _authzSession(authzSession),
+ _saslStep(0),
+ _conversationId(0),
+ _autoAuthorize(false),
+ _done(false) {
+ }
+
+ SaslAuthenticationSession::~SaslAuthenticationSession() {};
+
+ StringData SaslAuthenticationSession::getAuthenticationDatabase() const {
+ if (Command::testCommandsEnabled &&
+ _authenticationDatabase == "admin" &&
+ getPrincipalId() == internalSecurity.user->getName().getUser()) {
+ // Allows authenticating as the internal user against the admin database. This is to
+ // support the auth passthrough test framework on mongos (since you can't use the local
+ // database on a mongos, so you can't auth as the internal user without this).
+ return internalSecurity.user->getName().getDB();
+ } else {
+ return _authenticationDatabase;
+ }
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_authentication_session.h b/src/mongo/db/auth/sasl_authentication_session.h
new file mode 100644
index 00000000000..e4b8ac42655
--- /dev/null
+++ b/src/mongo/db/auth/sasl_authentication_session.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012 10gen, Inc. All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/db/auth/authentication_session.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/platform/cstdint.h"
+#include "mongo/stdx/functional.h"
+
+namespace mongo {
+
+ class AuthorizationSession;
+ class OperationContext;
+
+ /**
+ * Authentication session data for the server side of SASL authentication.
+ */
+ class SaslAuthenticationSession : public AuthenticationSession {
+ MONGO_DISALLOW_COPYING(SaslAuthenticationSession);
+ public:
+ typedef stdx::function<SaslAuthenticationSession* (AuthorizationSession*)>
+ SaslSessionFactoryFn;
+ static SaslSessionFactoryFn create;
+
+ // Mechanism name constants.
+ static const char mechanismCRAMMD5[];
+ static const char mechanismDIGESTMD5[];
+ static const char mechanismSCRAMSHA1[];
+ static const char mechanismGSSAPI[];
+ static const char mechanismPLAIN[];
+
+ explicit SaslAuthenticationSession(AuthorizationSession* authSession);
+ virtual ~SaslAuthenticationSession();
+
+ /**
+ * Start the server side of a SASL authentication.
+ *
+ * "authenticationDatabase" is the database against which the user is authenticating.
+ * "mechanism" is the SASL mechanism to use.
+ * "serviceName" is the SASL service name to use.
+ * "serviceHostname" is the FQDN of this server.
+ * "conversationId" is the conversation identifier to use for this session.
+ *
+ * If "autoAuthorize" is set to true, the server will automatically acquire all privileges
+ * for a successfully authenticated user. If it is false, the client will need to
+ * explicilty acquire privileges on resources it wishes to access.
+ *
+ * Must be called only once on an instance.
+ */
+ virtual Status start(const StringData& authenticationDatabase,
+ const StringData& mechanism,
+ const StringData& serviceName,
+ const StringData& serviceHostname,
+ int64_t conversationId,
+ bool autoAuthorize) = 0;
+
+ /**
+ * Perform one step of the server side of the authentication session,
+ * consuming "inputData" and producing "*outputData".
+ *
+ * A return of Status::OK() indiciates succesful progress towards authentication.
+ * Any other return code indicates that authentication has failed.
+ *
+ * Must not be called before start().
+ */
+ virtual Status step(const StringData& inputData, std::string* outputData) = 0;
+
+ /**
+ * Returns the the operation context associated with the currently executing command.
+ * Authentication commands must set this on their associated
+ * SaslAuthenticationSession.
+ */
+ OperationContext* getOpCtxt() const { return _txn; }
+ void setOpCtxt(OperationContext* txn) { _txn = txn; }
+
+ /**
+ * Gets the name of the database against which this authentication conversation is running.
+ *
+ * Not meaningful before a successful call to start().
+ */
+ StringData getAuthenticationDatabase() const;
+
+ /**
+ * Get the conversation id for this authentication session.
+ *
+ * Must not be called before start().
+ */
+ int64_t getConversationId() const { return _conversationId; }
+
+ /**
+ * If the last call to step() returned Status::OK(), this method returns true if the
+ * authentication conversation has completed, from the server's perspective. If it returns
+ * false, the server expects more input from the client. If the last call to step() did not
+ * return Status::OK(), returns true.
+ *
+ * Behavior is undefined if step() has not been called.
+ */
+ bool isDone() const { return _done; }
+
+ /**
+ * Gets the string identifier of the principal being authenticated.
+ *
+ * Returns the empty string if the session does not yet know the identity being
+ * authenticated.
+ */
+ virtual std::string getPrincipalId() const = 0;
+
+ /**
+ * Gets the name of the SASL mechanism in use.
+ *
+ * Returns "" if start() has not been called or if start() did not return Status::OK().
+ */
+ virtual const char* getMechanism() const = 0;
+
+ /**
+ * Returns true if automatic privilege acquisition should be used for this principal, after
+ * authentication. Not meaningful before a successful call to start().
+ */
+ bool shouldAutoAuthorize() const { return _autoAuthorize; }
+
+ AuthorizationSession* getAuthorizationSession() { return _authzSession; }
+
+ protected:
+ OperationContext* _txn;
+ AuthorizationSession* _authzSession;
+ std::string _authenticationDatabase;
+ std::string _serviceName;
+ std::string _serviceHostname;
+ int _saslStep;
+ int64_t _conversationId;
+ bool _autoAuthorize;
+ bool _done;
+ };
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_commands.cpp b/src/mongo/db/auth/sasl_commands.cpp
new file mode 100644
index 00000000000..441744bfda5
--- /dev/null
+++ b/src/mongo/db/auth/sasl_commands.cpp
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2012 10gen, Inc. All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl
+
+#include "mongo/platform/basic.h"
+
+#include <boost/scoped_ptr.hpp>
+
+#include "mongo/base/init.h"
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/bson/util/bson_extract.h"
+#include "mongo/client/sasl_client_authenticate.h"
+#include "mongo/db/audit.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/auth/authz_manager_external_state_mock.h"
+#include "mongo/db/auth/authz_session_external_state_mock.h"
+#include "mongo/db/auth/mongo_authentication_session.h"
+#include "mongo/db/auth/sasl_authentication_session.h"
+#include "mongo/db/auth/sasl_options.h"
+#include "mongo/db/client_basic.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/commands/authentication_commands.h"
+#include "mongo/util/base64.h"
+#include "mongo/util/log.h"
+#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/sequence_util.h"
+#include "mongo/util/stringutils.h"
+
+namespace mongo {
+namespace {
+
+ const bool autoAuthorizeDefault = true;
+
+ class CmdSaslStart : public Command {
+ public:
+ CmdSaslStart();
+ virtual ~CmdSaslStart();
+
+ virtual void addRequiredPrivileges(
+ const std::string&, const BSONObj&, std::vector<Privilege>*) {}
+
+ virtual bool run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& ignored,
+ BSONObjBuilder& result,
+ bool fromRepl);
+
+ virtual void help(stringstream& help) const;
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool slaveOk() const { return true; }
+ virtual bool requiresAuth() { return false; }
+
+ };
+
+ class CmdSaslContinue : public Command {
+ public:
+ CmdSaslContinue();
+ virtual ~CmdSaslContinue();
+
+ virtual void addRequiredPrivileges(
+ const std::string&, const BSONObj&, std::vector<Privilege>*) {}
+
+ virtual bool run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& ignored,
+ BSONObjBuilder& result,
+ bool fromRepl);
+
+ virtual void help(stringstream& help) const;
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool slaveOk() const { return true; }
+ virtual bool requiresAuth() { return false; }
+ };
+
+ CmdSaslStart cmdSaslStart;
+ CmdSaslContinue cmdSaslContinue;
+ Status buildResponse(const SaslAuthenticationSession* session,
+ const std::string& responsePayload,
+ BSONType responsePayloadType,
+ BSONObjBuilder* result) {
+ result->appendIntOrLL(saslCommandConversationIdFieldName, session->getConversationId());
+ result->appendBool(saslCommandDoneFieldName, session->isDone());
+
+ if (responsePayload.size() > size_t(std::numeric_limits<int>::max())) {
+ return Status(ErrorCodes::InvalidLength, "Response payload too long");
+ }
+ if (responsePayloadType == BinData) {
+ result->appendBinData(saslCommandPayloadFieldName,
+ int(responsePayload.size()),
+ BinDataGeneral,
+ responsePayload.data());
+ }
+ else if (responsePayloadType == String) {
+ result->append(saslCommandPayloadFieldName, base64::encode(responsePayload));
+ }
+ else {
+ fassertFailed(4003);
+ }
+
+ return Status::OK();
+ }
+
+ Status extractConversationId(const BSONObj& cmdObj, int64_t* conversationId) {
+ BSONElement element;
+ Status status = bsonExtractField(cmdObj, saslCommandConversationIdFieldName, &element);
+ if (!status.isOK())
+ return status;
+
+ if (!element.isNumber()) {
+ return Status(ErrorCodes::TypeMismatch,
+ str::stream() << "Wrong type for field; expected number for " << element);
+ }
+ *conversationId = element.numberLong();
+ return Status::OK();
+ }
+
+ Status extractMechanism(const BSONObj& cmdObj, std::string* mechanism) {
+ return bsonExtractStringField(cmdObj, saslCommandMechanismFieldName, mechanism);
+ }
+
+ void addStatus(const Status& status, BSONObjBuilder* builder) {
+ builder->append("ok", status.isOK() ? 1.0: 0.0);
+ if (!status.isOK())
+ builder->append(saslCommandCodeFieldName, status.code());
+ if (!status.reason().empty())
+ builder->append(saslCommandErrmsgFieldName, status.reason());
+ }
+
+ Status doSaslStep(SaslAuthenticationSession* session,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) {
+
+ std::string payload;
+ BSONType type = EOO;
+ Status status = saslExtractPayload(cmdObj, &payload, &type);
+ if (!status.isOK())
+ return status;
+
+ std::string responsePayload;
+ // Passing in a payload and extracting a responsePayload
+ status = session->step(payload, &responsePayload);
+
+ if (!status.isOK()) {
+ log() << session->getMechanism() << " authentication failed for " <<
+ session->getPrincipalId() << " on " <<
+ session->getAuthenticationDatabase() << " ; " << status.toString() << std::endl;
+ // All the client needs to know is that authentication has failed.
+ return Status(ErrorCodes::AuthenticationFailed, "Authentication failed.");
+ }
+
+ status = buildResponse(session, responsePayload, type, result);
+ if (!status.isOK())
+ return status;
+
+ if (session->isDone()) {
+ UserName userName(session->getPrincipalId(), session->getAuthenticationDatabase());
+ status = session->getAuthorizationSession()->addAndAuthorizeUser(
+ session->getOpCtxt(), userName);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ log() << "Successfully authenticated as principal " <<
+ session->getPrincipalId() << " on " << session->getAuthenticationDatabase() <<
+ std::endl;
+ }
+ return Status::OK();
+ }
+
+ Status doSaslStart(SaslAuthenticationSession* session,
+ const std::string& db,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) {
+
+ bool autoAuthorize = false;
+ Status status = bsonExtractBooleanFieldWithDefault(cmdObj,
+ saslCommandAutoAuthorizeFieldName,
+ autoAuthorizeDefault,
+ &autoAuthorize);
+ if (!status.isOK())
+ return status;
+
+ std::string mechanism;
+ status = extractMechanism(cmdObj, &mechanism);
+ if (!status.isOK())
+ return status;
+
+
+ if (!sequenceContains(saslGlobalParams.authenticationMechanisms, mechanism)) {
+ result->append(saslCommandMechanismListFieldName,
+ saslGlobalParams.authenticationMechanisms);
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "Unsupported mechanism " << mechanism);
+ }
+
+ status = session->start(db,
+ mechanism,
+ saslGlobalParams.serviceName,
+ saslGlobalParams.hostName,
+ 1,
+ autoAuthorize);
+ if (!status.isOK())
+ return status;
+
+ return doSaslStep(session, cmdObj, result);
+ }
+
+ Status doSaslContinue(SaslAuthenticationSession* session,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) {
+
+ int64_t conversationId = 0;
+ Status status = extractConversationId(cmdObj, &conversationId);
+ if (!status.isOK())
+ return status;
+ if (conversationId != session->getConversationId())
+ return Status(ErrorCodes::ProtocolError, "sasl: Mismatched conversation id");
+
+ return doSaslStep(session, cmdObj, result);
+ }
+
+ CmdSaslStart::CmdSaslStart() : Command(saslStartCommandName) {}
+ CmdSaslStart::~CmdSaslStart() {}
+
+ void CmdSaslStart::help(std::stringstream& os) const {
+ os << "First step in a SASL authentication conversation.";
+ }
+
+ bool CmdSaslStart::run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& ignored,
+ BSONObjBuilder& result,
+ bool fromRepl) {
+
+ ClientBasic* client = ClientBasic::getCurrent();
+ client->resetAuthenticationSession(NULL);
+
+ SaslAuthenticationSession* session =
+ SaslAuthenticationSession::create(client->getAuthorizationSession());
+
+ boost::scoped_ptr<AuthenticationSession> sessionGuard(session);
+
+ session->setOpCtxt(txn);
+
+ Status status = doSaslStart(session, db, cmdObj, &result);
+ addStatus(status, &result);
+
+ if (session->isDone()) {
+ audit::logAuthentication(
+ client,
+ session->getMechanism(),
+ UserName(session->getPrincipalId(), db),
+ status.code());
+ }
+ else {
+ client->swapAuthenticationSession(sessionGuard);
+ }
+ return status.isOK();
+ }
+
+ CmdSaslContinue::CmdSaslContinue() : Command(saslContinueCommandName) {}
+ CmdSaslContinue::~CmdSaslContinue() {}
+
+ void CmdSaslContinue::help(std::stringstream& os) const {
+ os << "Subsequent steps in a SASL authentication conversation.";
+ }
+
+ bool CmdSaslContinue::run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& ignored,
+ BSONObjBuilder& result,
+ bool fromRepl) {
+
+ ClientBasic* client = ClientBasic::getCurrent();
+ boost::scoped_ptr<AuthenticationSession> sessionGuard(NULL);
+ client->swapAuthenticationSession(sessionGuard);
+
+ if (!sessionGuard || sessionGuard->getType() != AuthenticationSession::SESSION_TYPE_SASL) {
+ addStatus(Status(ErrorCodes::ProtocolError, "No SASL session state found"), &result);
+ return false;
+ }
+
+ SaslAuthenticationSession* session =
+ static_cast<SaslAuthenticationSession*>(sessionGuard.get());
+
+ if (session->getAuthenticationDatabase() != db) {
+ addStatus(Status(ErrorCodes::ProtocolError,
+ "Attempt to switch database target during SASL authentication."),
+ &result);
+ return false;
+ }
+
+ session->setOpCtxt(txn);
+
+ Status status = doSaslContinue(session, cmdObj, &result);
+ addStatus(status, &result);
+
+ if (session->isDone()) {
+ audit::logAuthentication(
+ client,
+ session->getMechanism(),
+ UserName(session->getPrincipalId(), db),
+ status.code());
+ }
+ else {
+ client->swapAuthenticationSession(sessionGuard);
+ }
+
+ return status.isOK();
+ }
+
+ // The CyrusSaslCommands Enterprise initializer is dependent on PreSaslCommands
+ MONGO_INITIALIZER_WITH_PREREQUISITES(PreSaslCommands,
+ ("NativeSaslServerCore"))
+ (InitializerContext*) {
+
+ if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "MONGODB-CR"))
+ CmdAuthenticate::disableAuthMechanism("MONGODB-CR");
+
+ if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "MONGODB-X509"))
+ CmdAuthenticate::disableAuthMechanism("MONGODB-X509");
+
+ return Status::OK();
+ }
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_conversation.cpp b/src/mongo/db/auth/sasl_conversation.cpp
new file mode 100644
index 00000000000..2c3af408b12
--- /dev/null
+++ b/src/mongo/db/auth/sasl_conversation.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 MongoDB Inc. All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/db/auth/sasl_conversation.h"
+
+#include <string>
+
+#include "mongo/util/log.h"
+
+namespace mongo {
+
+ SaslConversation::~SaslConversation() {};
+
+ std::string SaslConversation::getPrincipalId() {
+ return _user;
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_conversation.h b/src/mongo/db/auth/sasl_conversation.h
new file mode 100644
index 00000000000..e3e2ab43399
--- /dev/null
+++ b/src/mongo/db/auth/sasl_conversation.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 MongoDB Inc. All Rights Reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/db/auth/sasl_authentication_session.h"
+#include "mongo/db/auth/user.h"
+
+namespace mongo {
+
+ class SaslAuthenticationSession;
+ template <typename T> class StatusWith;
+
+ /**
+ * Abstract class for implementing the client or server-side
+ * of a SASL mechanism conversation.
+ */
+ class SaslConversation {
+ MONGO_DISALLOW_COPYING(SaslConversation);
+ public:
+ /**
+ * Implements the server side of a SASL authentication mechanism.
+ *
+ * "saslAuthSession" is the corresponding SASLAuthenticationSession.
+ * "saslAuthSession" must stay in scope until the SaslConversation's
+ * destructor completes.
+ *
+ **/
+ explicit SaslConversation(SaslAuthenticationSession* saslAuthSession) :
+ _saslAuthSession(saslAuthSession),
+ _user("") {}
+
+ virtual ~SaslConversation();
+
+ /**
+ * Performs one step of the server side of the authentication session,
+ * consuming "inputData" and producing "*outputData".
+ *
+ * A return of Status::OK() indicates successful progress towards authentication.
+ * A return of !Status::OK() indicates failed authentication
+ *
+ * A return of true means that the authentication process has finished.
+ * A return of false means that the authentication process has more steps.
+ *
+ */
+ virtual StatusWith<bool> step(const StringData& inputData, std::string* outputData) = 0;
+
+ /**
+ * Gets the SASL principal id (user name) for the conversation
+ **/
+ std::string getPrincipalId();
+
+ protected:
+ SaslAuthenticationSession* _saslAuthSession;
+ std::string _user;
+ };
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_options.cpp b/src/mongo/db/auth/sasl_options.cpp
new file mode 100644
index 00000000000..40fc536656c
--- /dev/null
+++ b/src/mongo/db/auth/sasl_options.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2013 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl
+
+#include "mongo/db/auth/sasl_options.h"
+
+#include "mongo/base/status.h"
+#include "mongo/db/server_parameters.h"
+#include "mongo/util/log.h"
+#include "mongo/util/options_parser/startup_option_init.h"
+#include "mongo/util/options_parser/startup_options.h"
+
+namespace mongo {
+
+ SASLGlobalParams saslGlobalParams;
+
+ // Authentication mechanisms supported by default
+ SASLGlobalParams::SASLGlobalParams() {
+ authenticationMechanisms.push_back("MONGODB-CR");
+ authenticationMechanisms.push_back("MONGODB-X509");
+ authenticationMechanisms.push_back("SCRAM-SHA-1");
+ }
+
+ Status addSASLOptions(moe::OptionSection* options) {
+
+ moe::OptionSection saslOptions("SASL Options");
+
+ saslOptions.addOptionChaining("security.authenticationMechanisms", "",
+ moe::StringVector, "List of supported authentication mechanisms. "
+ "Default is MONGODB-CR, SCRAM-SHA-1 and MONGODB-X509.")
+ .setSources(moe::SourceYAMLConfig);
+
+ saslOptions.addOptionChaining("security.sasl.hostName", "", moe::String,
+ "Fully qualified server domain name")
+ .setSources(moe::SourceYAMLConfig);
+
+ saslOptions.addOptionChaining("security.sasl.serviceName", "", moe::String,
+ "Registered name of the service using SASL")
+ .setSources(moe::SourceYAMLConfig);
+
+ saslOptions.addOptionChaining("security.sasl.saslauthdSocketPath", "", moe::String,
+ "Path to Unix domain socket file for saslauthd")
+ .setSources(moe::SourceYAMLConfig);
+
+ Status ret = options->addSection(saslOptions);
+ if (!ret.isOK()) {
+ log() << "Failed to add sasl option section: " << ret.toString();
+ return ret;
+ }
+
+ return Status::OK();
+ }
+
+ Status storeSASLOptions(const moe::Environment& params) {
+
+ bool haveAuthenticationMechanisms = false;
+ bool haveHostName = false;
+ bool haveServiceName = false;
+ bool haveAuthdPath = false;
+
+ // Check our setParameter options first so that these values can be properly overridden via
+ // the command line even though the options have different names.
+ if (params.count("setParameter")) {
+ std::map<std::string, std::string> parameters =
+ params["setParameter"].as<std::map<std::string, std::string> >();
+ for (std::map<std::string, std::string>::iterator parametersIt = parameters.begin();
+ parametersIt != parameters.end(); parametersIt++) {
+ if (parametersIt->first == "authenticationMechanisms") {
+ haveAuthenticationMechanisms = true;
+ }
+ else if (parametersIt->first == "saslHostName") {
+ haveHostName = true;
+ }
+ else if (parametersIt->first == "saslServiceName") {
+ haveServiceName = true;
+ }
+ else if (parametersIt->first == "saslauthdPath") {
+ haveAuthdPath = true;
+ }
+ }
+ }
+
+ if (params.count("security.authenticationMechanisms") &&
+ !haveAuthenticationMechanisms) {
+ saslGlobalParams.authenticationMechanisms =
+ params["security.authenticationMechanisms"].as<std::vector<std::string> >();
+ }
+ if (params.count("security.sasl.hostName") && !haveHostName) {
+ saslGlobalParams.hostName =
+ params["security.sasl.hostName"].as<std::string>();
+ }
+ if (params.count("security.sasl.serviceName") && !haveServiceName) {
+ saslGlobalParams.serviceName =
+ params["security.sasl.serviceName"].as<std::string>();
+ }
+ if (params.count("security.sasl.saslauthdSocketPath") && !haveAuthdPath) {
+ saslGlobalParams.authdPath =
+ params["security.sasl.saslauthdSocketPath"].as<std::string>();
+ }
+
+ return Status::OK();
+ }
+
+ MONGO_MODULE_STARTUP_OPTIONS_REGISTER(SASLOptions)(InitializerContext* context) {
+ return addSASLOptions(&moe::startupOptions);
+ }
+
+ MONGO_STARTUP_OPTIONS_STORE(SASLOptions)(InitializerContext* context) {
+ return storeSASLOptions(moe::startupOptionsParsed);
+ }
+
+ // SASL Startup Parameters, making them settable via setParameter on the command line or in the
+ // legacy INI config file. None of these parameters are modifiable at runtime.
+ ExportedServerParameter<std::vector<std::string> > SASLAuthenticationMechanismsSetting(
+ ServerParameterSet::getGlobal(),
+ "authenticationMechanisms",
+ &saslGlobalParams.authenticationMechanisms,
+ true, // Change at startup
+ false); // Change at runtime
+
+ ExportedServerParameter<std::string> SASLHostNameSetting(ServerParameterSet::getGlobal(),
+ "saslHostName",
+ &saslGlobalParams.hostName,
+ true, // Change at startup
+ false); // Change at runtime
+
+ ExportedServerParameter<std::string> SASLServiceNameSetting(ServerParameterSet::getGlobal(),
+ "saslServiceName",
+ &saslGlobalParams.serviceName,
+ true, // Change at startup
+ false); // Change at runtime
+
+ ExportedServerParameter<std::string> SASLAuthdPathSetting(ServerParameterSet::getGlobal(),
+ "saslauthdPath",
+ &saslGlobalParams.authdPath,
+ true, // Change at startup
+ false); // Change at runtime
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_options.h b/src/mongo/db/auth/sasl_options.h
new file mode 100644
index 00000000000..77ca66ad1d0
--- /dev/null
+++ b/src/mongo/db/auth/sasl_options.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "mongo/base/status.h"
+
+namespace mongo {
+
+namespace optionenvironment {
+ class OptionSection;
+ class Environment;
+} // namespace optionenvironment
+
+ namespace moe = optionenvironment;
+
+ struct SASLGlobalParams {
+
+ std::vector<std::string> authenticationMechanisms;
+ std::string hostName;
+ std::string serviceName;
+ std::string authdPath;
+
+ SASLGlobalParams();
+ };
+
+ extern SASLGlobalParams saslGlobalParams;
+
+ Status addSASLOptions(moe::OptionSection* options);
+
+ Status storeSASLOptions(const moe::Environment& params);
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_plain_server_conversation.cpp b/src/mongo/db/auth/sasl_plain_server_conversation.cpp
new file mode 100644
index 00000000000..6bf06682851
--- /dev/null
+++ b/src/mongo/db/auth/sasl_plain_server_conversation.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/db/auth/sasl_plain_server_conversation.h"
+
+#include "mongo/db/auth/sasl_authentication_session.h"
+#include "mongo/util/password_digest.h"
+#include "mongo/util/text.h"
+
+namespace mongo {
+
+ SaslPLAINServerConversation::SaslPLAINServerConversation(
+ SaslAuthenticationSession* saslAuthSession) :
+ SaslConversation(saslAuthSession) {
+ }
+
+ SaslPLAINServerConversation::~SaslPLAINServerConversation() {};
+
+ StatusWith<bool> SaslPLAINServerConversation::step(const StringData& inputData,
+ std::string* outputData) {
+ // Expecting user input on the form: user\0user\0pwd
+ std::string input = inputData.toString();
+ std::string pwd = "";
+
+ try {
+ _user = input.substr(0, inputData.find('\0'));
+ pwd = input.substr(inputData.find('\0', _user.size()+1)+1);
+ }
+ catch (std::out_of_range& exception) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ mongoutils::str::stream() << "Incorrectly formatted PLAIN client message");
+ }
+
+ User* userObj;
+ // The authentication database is also the source database for the user.
+ Status status = _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
+ acquireUser(_saslAuthSession->getOpCtxt(),
+ UserName(_user, _saslAuthSession->getAuthenticationDatabase()),
+ &userObj);
+
+ if (!status.isOK()) {
+ return StatusWith<bool>(status);
+ }
+
+ const User::CredentialData creds = userObj->getCredentials();
+ _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
+ releaseUser(userObj);
+
+ std::string authDigest = createPasswordDigest(_user, pwd);
+
+ if (authDigest != creds.password) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ mongoutils::str::stream() << "Incorrect user name or password");
+ }
+
+ *outputData = "";
+
+ return StatusWith<bool>(true);
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_plain_server_conversation.h b/src/mongo/db/auth/sasl_plain_server_conversation.h
new file mode 100644
index 00000000000..855522f9566
--- /dev/null
+++ b/src/mongo/db/auth/sasl_plain_server_conversation.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/db/auth/sasl_conversation.h"
+
+namespace mongo {
+ /**
+ * Server side authentication session for SASL PLAIN.
+ */
+ class SaslPLAINServerConversation : public SaslConversation {
+ MONGO_DISALLOW_COPYING(SaslPLAINServerConversation);
+ public:
+ /**
+ * Implements the server side of a SASL PLAIN mechanism session.
+ *
+ **/
+ explicit SaslPLAINServerConversation(SaslAuthenticationSession* saslAuthSession);
+
+ virtual ~SaslPLAINServerConversation();
+
+ virtual StatusWith<bool> step(const StringData& inputData, std::string* outputData);
+ };
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp b/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
new file mode 100644
index 00000000000..3bdebaf9677
--- /dev/null
+++ b/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/auth/sasl_scramsha1_server_conversation.h"
+
+#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#ifdef MONGO_SSL
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#endif
+
+#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"
+
+namespace mongo {
+ SaslSCRAMSHA1ServerConversation::SaslSCRAMSHA1ServerConversation(
+ SaslAuthenticationSession* saslAuthSession) :
+ SaslConversation(saslAuthSession),
+ _step(0),
+ _authMessage(""),
+ _nonce("") {
+ }
+
+ StatusWith<bool> SaslSCRAMSHA1ServerConversation::step(const StringData& inputData,
+ std::string* outputData) {
+
+ std::vector<std::string> input = StringSplitter::split(inputData.toString(), ",");
+ _step++;
+
+ if (_step > 3 || _step <= 0) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ mongoutils::str::stream() << "Invalid SCRAM-SHA-1 authentication step: " << _step);
+ }
+ if (_step == 1) {
+ return _firstStep(input, outputData);
+ }
+ if (_step == 2) {
+ return _secondStep(input, outputData);
+ }
+
+ *outputData = "";
+
+ return StatusWith<bool>(true);
+ }
+
+ /*
+ * RFC 5802 specifies that in SCRAM user names characters ',' and '=' are encoded as
+ * =2C and =3D respectively.
+ */
+ static void decodeSCRAMUsername(std::string& user) {
+ boost::replace_all(user, "=2C", ",");
+ boost::replace_all(user, "=3D", "=");
+ }
+
+ /*
+ * Parse client-first-message of the form:
+ * n,a=authzid,n=encoded-username,r=client-nonce
+ *
+ * Generate server-first-message on the form:
+ * r=client-nonce|server-nonce,s=user-salt,i=iteration-count
+ *
+ * NOTE: we are ignoring the authorization ID part of the message
+ */
+ StatusWith<bool> SaslSCRAMSHA1ServerConversation::_firstStep(std::vector<string>& input,
+ std::string* outputData) {
+#ifndef MONGO_SSL
+ return StatusWith<bool>(ErrorCodes::InternalError,
+ "The server is not compiled with SSL support");
+#else
+ std::string authzId = "";
+
+ if (input.size() == 4) {
+ /* The second entry a=authzid is optional. If provided it will be
+ * validated against the encoded username.
+ *
+ * The two allowed input forms are:
+ * n,,n=encoded-username,r=client-nonce
+ * n,a=authzid,n=encoded-username,r=client-nonce
+ */
+ if (!str::startsWith(input[1], "a=") || input[1].size() < 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 authzid: " << input[1]);
+ }
+ authzId = input[1].substr(2);
+ input.erase(input.begin() + 1);
+ }
+
+ if (input.size() != 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect number of arguments for first SCRAM-SHA-1 client message, got " <<
+ input.size() << " expected 4");
+ }
+ else if (input[0] != "n") {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 client message prefix: " << input[0]);
+ }
+ else if (!str::startsWith(input[1], "n=") || input[1].size() < 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 user name: " << input[1]);
+ }
+ else if(!str::startsWith(input[2], "r=") || input[2].size() < 6) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 client nonce: " << input[2]);
+ }
+
+ // add client-first-message-bare to _authMessage
+ _authMessage += input[1] + "," + input[2] + ",";
+
+ _user = input[1].substr(2);
+ if (!authzId.empty() && _user != authzId) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "SCRAM-SHA-1 user name " << _user << " does not match authzid " << authzId);
+ }
+
+ decodeSCRAMUsername(_user);
+ std::string clientNonce = input[2].substr(2);
+
+ // The authentication database is also the source database for the user.
+ User* userObj;
+ Status status = _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
+ acquireUser(_saslAuthSession->getOpCtxt(),
+ UserName(_user, _saslAuthSession->getAuthenticationDatabase()),
+ &userObj);
+
+ if (!status.isOK()) {
+ return StatusWith<bool>(status);
+ }
+
+ _creds = userObj->getCredentials();
+ _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
+ releaseUser(userObj);
+
+ // Generate server-first-message
+ // Create text-based nonce as base64 encoding of a binary blob of length multiple of 3
+ const int nonceLenQWords = 3;
+ uint64_t binaryNonce[nonceLenQWords];
+
+ scoped_ptr<SecureRandom> sr(SecureRandom::create());
+
+ binaryNonce[0] = sr->nextInt64();
+ binaryNonce[1] = sr->nextInt64();
+ binaryNonce[2] = sr->nextInt64();
+
+ _nonce = clientNonce +
+ base64::encode(reinterpret_cast<char*>(binaryNonce), sizeof(binaryNonce));
+ StringBuilder sb;
+ sb << "r=" << _nonce <<
+ ",s=" << _creds.scram.salt <<
+ ",i=" << _creds.scram.iterationCount;
+ *outputData = sb.str();
+
+ // add server-first-message to authMessage
+ _authMessage += *outputData + ",";
+
+ return StatusWith<bool>(false);
+#endif // MONGO_SSL
+ }
+
+ /**
+ * Parse client-final-message of the form:
+ * c=channel-binding(base64),r=client-nonce|server-nonce,p=ClientProof
+ *
+ * Generate successful authentication server-final-message on the form:
+ * v=ServerSignature
+ *
+ * or failed authentication server-final-message on the form:
+ * e=message
+ *
+ * NOTE: we are ignoring the channel binding part of the message
+ **/
+ StatusWith<bool> SaslSCRAMSHA1ServerConversation::_secondStep(const std::vector<string>& input,
+ std::string* outputData) {
+#ifndef MONGO_SSL
+ return StatusWith<bool>(ErrorCodes::InternalError,
+ "The server is not compiled with SSL support");
+#else
+ if (input.size() != 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect number of arguments for second SCRAM-SHA-1 client message, got " <<
+ input.size() << " expected 3");
+ }
+ else if (!str::startsWith(input[0], "c=") || input[0].size() < 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 channel binding: " << input[0]);
+ }
+ else if (!str::startsWith(input[1], "r=") || input[1].size() < 6) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 client|server nonce: " << input[1]);
+ }
+ else if(!str::startsWith(input[2], "p=") || input[2].size() < 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 ClientProof: " << input[2]);
+ }
+
+ // add client-final-message-without-proof to authMessage
+ _authMessage += input[0] + "," + input[1];
+
+ // Concatenated nonce sent by client should equal the one in server-first-message
+ std::string nonce = input[1].substr(2);
+ if (nonce != _nonce) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Unmatched SCRAM-SHA-1 nonce received from client in second step, expected " <<
+ _nonce << " but received " << nonce);
+ }
+
+ std::string clientProof = input[2].substr(2);
+
+ // Do server side computations, compare storedKeys and generate client-final-message
+ // AuthMessage := client-first-message-bare + "," +
+ // server-first-message + "," +
+ // client-final-message-without-proof
+ // ClientSignature := HMAC(StoredKey, AuthMessage)
+ // ClientKey := ClientSignature XOR ClientProof
+ // ServerSignature := HMAC(ServerKey, AuthMessage)
+
+ const unsigned int scramHashSize = 20;
+ unsigned int hashLen = 0;
+ unsigned char clientSignature[scramHashSize];
+
+ std::string decodedStoredKey = base64::decode(_creds.scram.storedKey);
+ // ClientSignature := HMAC(StoredKey, AuthMessage)
+ fassert(18657, HMAC(EVP_sha1(),
+ reinterpret_cast<const unsigned char*>(decodedStoredKey.c_str()),
+ scramHashSize,
+ reinterpret_cast<const unsigned char*>(_authMessage.c_str()),
+ _authMessage.size(),
+ clientSignature,
+ &hashLen));
+
+ fassert(18658, hashLen == scramHashSize);
+
+ const unsigned char *decodedClientProof =
+ reinterpret_cast<const unsigned char*>(base64::decode(clientProof).c_str());
+
+ // ClientKey := ClientSignature XOR ClientProof
+ unsigned char clientKey[scramHashSize];
+ for(size_t i=0; i<scramHashSize; i++) {
+ clientKey[i] = clientSignature[i]^decodedClientProof[i];
+ }
+
+ // StoredKey := H(ClientKey)
+ unsigned char computedStoredKey[scramHashSize];
+ fassert(18659, SHA1(clientKey, scramHashSize, computedStoredKey));
+
+ if (memcmp(decodedStoredKey.c_str(), computedStoredKey, scramHashSize) != 0) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ mongoutils::str::stream() <<
+ "SCRAM-SHA-1 auhentication failed, storedKey mismatch");
+ }
+
+ // ServerSignature := HMAC(ServerKey, AuthMessage)
+ unsigned char serverSignature[scramHashSize];
+ std::string decodedServerKey = base64::decode(_creds.scram.serverKey);
+ fassert(18660, HMAC(EVP_sha1(),
+ reinterpret_cast<const unsigned char*>(decodedServerKey.c_str()),
+ scramHashSize,
+ reinterpret_cast<const unsigned char*>(_authMessage.c_str()),
+ _authMessage.size(),
+ serverSignature,
+ &hashLen));
+
+ fassert(18661, hashLen == scramHashSize);
+
+ StringBuilder sb;
+ sb << "v=" << base64::encode(reinterpret_cast<char*>(serverSignature), scramHashSize);
+ *outputData = sb.str();
+
+ return StatusWith<bool>(false);
+#endif // MONGO_SSL
+ }
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_scramsha1_server_conversation.h b/src/mongo/db/auth/sasl_scramsha1_server_conversation.h
new file mode 100644
index 00000000000..aa3d3f844ae
--- /dev/null
+++ b/src/mongo/db/auth/sasl_scramsha1_server_conversation.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <boost/scoped_ptr.hpp>
+#include <string>
+#include <vector>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/db/auth/sasl_conversation.h"
+
+namespace mongo {
+ /**
+ * Server side authentication session for SASL PLAIN.
+ */
+ class SaslSCRAMSHA1ServerConversation : public SaslConversation {
+ MONGO_DISALLOW_COPYING(SaslSCRAMSHA1ServerConversation);
+ public:
+ /**
+ * Implements the server side of a SASL PLAIN mechanism session.
+ **/
+ explicit SaslSCRAMSHA1ServerConversation(SaslAuthenticationSession* saslAuthSession);
+
+ virtual ~SaslSCRAMSHA1ServerConversation() {};
+
+ /**
+ * Take one step in a SCRAM-SHA-1 conversation.
+ *
+ * @return !Status::OK() if auth failed. The boolean part indicates if the
+ * authentication conversation is finished or not.
+ *
+ **/
+ virtual StatusWith<bool> step(const StringData& inputData, std::string* outputData);
+
+ private:
+ /**
+ * Parse client-first-message and generate server-first-message
+ **/
+ StatusWith<bool> _firstStep(std::vector<std::string>& input, std::string* outputData);
+
+ /**
+ * Parse client-final-message and generate server-final-message
+ **/
+ StatusWith<bool> _secondStep(const std::vector<string>& input, std::string* outputData);
+
+ int _step;
+ std::string _authMessage;
+ User::CredentialData _creds;
+
+ // client and server nonce concatenated
+ std::string _nonce;
+ };
+
+} // namespace mongo