summaryrefslogtreecommitdiff
path: root/src/mongo/client
diff options
context:
space:
mode:
authorAndreas Nilsson <andreas.nilsson@10gen.com>2014-09-09 16:28:26 -0400
committerAndreas Nilsson <andreas.nilsson@10gen.com>2014-09-10 16:45:02 -0400
commitac611b47d4f9edc7a576e52735a0edf0bde9f989 (patch)
tree6f380b298a9b477665601c9822b3d17ada26d372 /src/mongo/client
parent2bae1279afcab72bd20967d65c8e350f8c2cffb9 (diff)
downloadmongo-ac611b47d4f9edc7a576e52735a0edf0bde9f989.tar.gz
SERVER-7596 Native SCRAM-SHA-1 client/shell implementation
Diffstat (limited to 'src/mongo/client')
-rw-r--r--src/mongo/client/native_sasl_client_session.cpp33
-rw-r--r--src/mongo/client/native_sasl_client_session.h7
-rw-r--r--src/mongo/client/sasl_client_conversation.cpp35
-rw-r--r--src/mongo/client/sasl_client_conversation.h78
-rw-r--r--src/mongo/client/sasl_plain_client_conversation.cpp64
-rw-r--r--src/mongo/client/sasl_plain_client_conversation.h56
-rw-r--r--src/mongo/client/sasl_scramsha1_client_conversation.cpp252
-rw-r--r--src/mongo/client/sasl_scramsha1_client_conversation.h88
8 files changed, 608 insertions, 5 deletions
diff --git a/src/mongo/client/native_sasl_client_session.cpp b/src/mongo/client/native_sasl_client_session.cpp
index 705626dc106..57e33eb9639 100644
--- a/src/mongo/client/native_sasl_client_session.cpp
+++ b/src/mongo/client/native_sasl_client_session.cpp
@@ -30,6 +30,9 @@
#include "mongo/client/native_sasl_client_session.h"
#include "mongo/base/init.h"
+#include "mongo/client/sasl_client_conversation.h"
+#include "mongo/client/sasl_plain_client_conversation.h"
+#include "mongo/client/sasl_scramsha1_client_conversation.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
@@ -49,18 +52,38 @@ namespace {
NativeSaslClientSession::NativeSaslClientSession() :
SaslClientSession(),
_step(0),
- _done(false) {
+ _done(false),
+ _saslConversation(NULL) {
}
NativeSaslClientSession::~NativeSaslClientSession() {}
Status NativeSaslClientSession::initialize() {
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << "SASL authentication not supported in client");
+ if (_saslConversation)
+ return Status(ErrorCodes::AlreadyInitialized,
+ "Cannot reinitialize NativeSaslClientSession.");
+
+ std::string mechanism = getParameter(parameterMechanism).toString();
+ if (mechanism == "PLAIN") {
+ _saslConversation.reset(new SaslPLAINClientConversation(this));
+ }
+ else if (mechanism == "SCRAM-SHA-1") {
+ _saslConversation.reset(new SaslSCRAMSHA1ClientConversation(this));
+ }
+ else {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "SASL mechanism " << mechanism <<
+ "is not supported");
+ }
+
+ return Status::OK();
}
Status NativeSaslClientSession::step(const StringData& inputData, std::string* outputData) {
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << "SASL authentication not supported in client");
+ StatusWith<bool> status = _saslConversation->step(inputData, outputData);
+ if (status.isOK()) {
+ _done = status.getValue();
+ }
+ return status.getStatus();
}
} // namespace
diff --git a/src/mongo/client/native_sasl_client_session.h b/src/mongo/client/native_sasl_client_session.h
index d80b9cc57ed..3bb83230025 100644
--- a/src/mongo/client/native_sasl_client_session.h
+++ b/src/mongo/client/native_sasl_client_session.h
@@ -25,10 +25,14 @@
* then also delete it in the license file.
*/
+#include <boost/scoped_ptr.hpp>
+
#include "mongo/client/sasl_client_session.h"
namespace mongo {
+ class SaslClientConversation;
+
/**
* Implementation of the client side of a SASL authentication conversation using the
* native SASL implementation.
@@ -52,6 +56,9 @@ namespace mongo {
/// See isDone().
bool _done;
+
+ /// The client side of a SASL authentication conversation.
+ boost::scoped_ptr<SaslClientConversation> _saslConversation;
};
} // namespace mongo
diff --git a/src/mongo/client/sasl_client_conversation.cpp b/src/mongo/client/sasl_client_conversation.cpp
new file mode 100644
index 00000000000..637d6b14f5f
--- /dev/null
+++ b/src/mongo/client/sasl_client_conversation.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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/client/sasl_client_conversation.h"
+
+namespace mongo {
+
+ SaslClientConversation::~SaslClientConversation() {};
+
+} // namespace mongo
diff --git a/src/mongo/client/sasl_client_conversation.h b/src/mongo/client/sasl_client_conversation.h
new file mode 100644
index 00000000000..f71e7ea0ad0
--- /dev/null
+++ b/src/mongo/client/sasl_client_conversation.h
@@ -0,0 +1,78 @@
+/*
+ * 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 "mongo/base/disallow_copying.h"
+#include "mongo/base/string_data.h"
+
+namespace mongo {
+
+ class SaslClientSession;
+ template <typename T> class StatusWith;
+
+ /**
+ * Abstract class for implementing the clent-side
+ * of a SASL mechanism conversation.
+ */
+ class SaslClientConversation {
+ MONGO_DISALLOW_COPYING(SaslClientConversation);
+ public:
+ /**
+ * Implements the client side of a SASL authentication mechanism.
+ *
+ * "saslClientSession" is the corresponding SASLClientSession.
+ * "saslClientSession" must stay in scope until the SaslClientConversation's
+ * destructor completes.
+ *
+ **/
+ explicit SaslClientConversation(SaslClientSession* saslClientSession) :
+ _saslClientSession(saslClientSession) {}
+
+ virtual ~SaslClientConversation();
+
+ /**
+ * Performs one step of the client 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;
+
+ protected:
+ SaslClientSession* _saslClientSession;
+ };
+
+} // namespace mongo
diff --git a/src/mongo/client/sasl_plain_client_conversation.cpp b/src/mongo/client/sasl_plain_client_conversation.cpp
new file mode 100644
index 00000000000..b3436cc836e
--- /dev/null
+++ b/src/mongo/client/sasl_plain_client_conversation.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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/client/sasl_plain_client_conversation.h"
+
+#include "mongo/base/status_with.h"
+#include "mongo/client/sasl_client_session.h"
+#include "mongo/util/log.h"
+#include "mongo/util/password_digest.h"
+#include "mongo/util/text.h"
+
+namespace mongo {
+
+ SaslPLAINClientConversation::SaslPLAINClientConversation(
+ SaslClientSession* saslClientSession) :
+ SaslClientConversation(saslClientSession) {
+ }
+
+ SaslPLAINClientConversation::~SaslPLAINClientConversation() {};
+
+ StatusWith<bool> SaslPLAINClientConversation::step(const StringData& inputData,
+ std::string* outputData) {
+ // Create PLAIN message on the form: user\0user\0pwd
+
+ StringBuilder sb;
+ sb << _saslClientSession->getParameter(SaslClientSession::parameterUser).toString() <<
+ '\0' <<
+ _saslClientSession->getParameter(SaslClientSession::parameterUser).toString() <<
+ '\0' <<
+ _saslClientSession->getParameter(SaslClientSession::parameterPassword).toString();
+
+ *outputData = sb.str();
+
+ return StatusWith<bool>(true);
+ }
+
+} // namespace mongo
diff --git a/src/mongo/client/sasl_plain_client_conversation.h b/src/mongo/client/sasl_plain_client_conversation.h
new file mode 100644
index 00000000000..ffd3f46b810
--- /dev/null
+++ b/src/mongo/client/sasl_plain_client_conversation.h
@@ -0,0 +1,56 @@
+/*
+ * 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 "mongo/base/disallow_copying.h"
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/client/sasl_client_conversation.h"
+
+namespace mongo {
+ /**
+ * Client side authentication session for SASL PLAIN.
+ */
+ class SaslPLAINClientConversation : public SaslClientConversation {
+ MONGO_DISALLOW_COPYING(SaslPLAINClientConversation);
+ public:
+ /**
+ * Implements the client side of a SASL PLAIN mechanism session.
+ *
+ **/
+ explicit SaslPLAINClientConversation(SaslClientSession* saslClientSession);
+
+ virtual ~SaslPLAINClientConversation();
+
+ virtual StatusWith<bool> step(const StringData& inputData, std::string* outputData);
+ };
+
+} // namespace mongo
diff --git a/src/mongo/client/sasl_scramsha1_client_conversation.cpp b/src/mongo/client/sasl_scramsha1_client_conversation.cpp
new file mode 100644
index 00000000000..1fdb171b52f
--- /dev/null
+++ b/src/mongo/client/sasl_scramsha1_client_conversation.cpp
@@ -0,0 +1,252 @@
+/*
+ * 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/client/sasl_scramsha1_client_conversation.h"
+
+#include <boost/algorithm/string/replace.hpp>
+#ifdef MONGO_SSL
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#endif
+
+#include "mongo/base/parse_number.h"
+#include "mongo/client/sasl_client_session.h"
+#include "mongo/platform/random.h"
+#include "mongo/util/base64.h"
+#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/password_digest.h"
+#include "mongo/util/text.h"
+
+namespace mongo {
+ SaslSCRAMSHA1ClientConversation::SaslSCRAMSHA1ClientConversation(
+ SaslClientSession* saslClientSession) :
+ SaslClientConversation(saslClientSession),
+ _step(0),
+ _authMessage(""),
+ _clientNonce("") {
+ }
+
+ SaslSCRAMSHA1ClientConversation::~SaslSCRAMSHA1ClientConversation(){
+ // clear the _saltedPassword memory
+ memset(_saltedPassword, 0, scram::hashSize);
+ }
+
+ StatusWith<bool> SaslSCRAMSHA1ClientConversation::step(const StringData& inputData,
+ std::string* outputData) {
+ std::vector<std::string> input = StringSplitter::split(inputData.toString(), ",");
+ _step++;
+
+ switch (_step) {
+ case 1:
+ return _firstStep(outputData);
+ case 2:
+ // Append server-first-message to _authMessage
+ _authMessage += inputData.toString() + ",";
+ return _secondStep(input, outputData);
+ case 3:
+ return _thirdStep(input, outputData);
+ default:
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ mongoutils::str::stream() <<
+ "Invalid SCRAM-SHA-1 authentication step: " << _step);
+ }
+ }
+
+ /*
+ * RFC 5802 specifies that in SCRAM user names characters ',' and '=' are encoded as
+ * =2C and =3D respectively.
+ */
+ static void encodeSCRAMUsername(std::string& user) {
+ boost::replace_all(user, ",", "=2C");
+ boost::replace_all(user, "=", "=3D");
+ }
+
+ /*
+ * Generate client-first-message of the form:
+ * n,a=authzid,n=encoded-username,r=client-nonce
+ */
+ StatusWith<bool> SaslSCRAMSHA1ClientConversation::_firstStep(std::string* outputData) {
+#ifndef MONGO_SSL
+ return StatusWith<bool>(ErrorCodes::InternalError,
+ "The server is not compiled with SSL support");
+#else
+ // 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();
+
+ std::string user =
+ _saslClientSession->getParameter(SaslClientSession::parameterUser).toString();
+ encodeSCRAMUsername(user);
+ std::string clientNonce = base64::encode(reinterpret_cast<char*>(binaryNonce),
+ sizeof(binaryNonce));
+
+ // Append client-first-message-bare to authMessage
+ _authMessage = "n=" + user + ",r=" + clientNonce + ",";
+
+ StringBuilder sb;
+ sb << "n,,n=" << user <<
+ ",r=" << clientNonce;
+ *outputData = sb.str();
+
+ return StatusWith<bool>(false);
+
+#endif // MONGO_SSL
+ }
+
+ /**
+ * Parse server-first-message on the form:
+ * r=client-nonce|server-nonce,s=user-salt,i=iteration-count
+ *
+ * Generate client-final-message of the form:
+ * c=channel-binding(base64),r=client-nonce|server-nonce,p=ClientProof
+ *
+ **/
+ StatusWith<bool> SaslSCRAMSHA1ClientConversation::_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 first SCRAM-SHA-1 server message, got " <<
+ input.size() << " expected 3");
+ }
+ else if (!str::startsWith(input[0], "r=") || input[0].size() < 2) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 client|server nonce: " << input[0]);
+ }
+ else if (!str::startsWith(input[1], "s=") || input[1].size() < 6) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 salt: " << input[1]);
+ }
+ else if(!str::startsWith(input[2], "i=") || input[2].size() < 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 iteration count: " << input[2]);
+ }
+
+ std::string nonce = input[0].substr(2);
+ if(!str::startsWith(nonce, _clientNonce)) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Server SCRAM-SHA-1 nonce does not match client nonce" << input[2]);
+ }
+
+ std::string salt = input[1].substr(2);
+ int iterationCount;
+
+ Status status = parseNumberFromStringWithBase(input[2].substr(2), 10, &iterationCount);
+ if (status != Status::OK()) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Failed to parse SCRAM-SHA-1 iteration count: " << input[2]);
+ }
+
+ // Append client-final-message-without-proof to _authMessage
+ _authMessage += "c=biws,r=" + nonce;
+
+ std::string decodedSalt;
+ try {
+ decodedSalt = base64::decode(salt);
+ }
+ catch (const DBException& ex) {
+ return StatusWith<bool>(ex.toStatus());
+ }
+
+ scram::generateSaltedPassword(
+ _saslClientSession->getParameter(SaslClientSession::parameterPassword),
+ reinterpret_cast<const unsigned char*>(decodedSalt.c_str()),
+ decodedSalt.size(),
+ iterationCount,
+ _saltedPassword);
+
+ std::string clientProof = scram::generateClientProof(_saltedPassword, _authMessage);
+
+ StringBuilder sb;
+ sb << "c=biws,r=" << nonce << ",p=" << clientProof;
+ *outputData = sb.str();
+
+ return StatusWith<bool>(false);
+#endif // MONGO_SSL
+ }
+
+ /**
+ * Verify server-final-message on the form:
+ * v=ServerSignature
+ *
+ * or failed authentication server-final-message on the form:
+ * e=message
+ **/
+ StatusWith<bool> SaslSCRAMSHA1ClientConversation::_thirdStep(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() != 1) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect number of arguments for final SCRAM-SHA-1 server message, got " <<
+ input.size() << " expected 1");
+ }
+ else if (input[0].size() < 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 server message length: " << input[0]);
+ }
+ else if (str::startsWith(input[0], "e=")) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed, mongoutils::str::stream() <<
+ "SCRAM-SHA-1 authentication failure: " << input[0].substr(2));
+ }
+ else if (!str::startsWith(input[0], "v=")) {
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Incorrect SCRAM-SHA-1 ServerSignature: " << input[0]);
+ }
+
+ bool validServerSignature =
+ scram::verifyServerSignature(_saltedPassword, _authMessage, input[0].substr(2));
+
+ if (!validServerSignature) {
+ *outputData = "e=Invalid server signature";
+ return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
+ "Client failed to verify SCRAM-SHA-1 ServerSignature, received " <<
+ input[0].substr(2));
+ }
+
+ *outputData = "";
+
+ return StatusWith<bool>(true);
+#endif // MONGO_SSL
+ }
+} // namespace mongo
diff --git a/src/mongo/client/sasl_scramsha1_client_conversation.h b/src/mongo/client/sasl_scramsha1_client_conversation.h
new file mode 100644
index 00000000000..668588bc03f
--- /dev/null
+++ b/src/mongo/client/sasl_scramsha1_client_conversation.h
@@ -0,0 +1,88 @@
+/*
+ * 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/client/sasl_client_conversation.h"
+#include "mongo/db/auth/mechanism_scram.h"
+#include "mongo/db/auth/user.h"
+
+namespace mongo {
+ /**
+ * Client side authentication session for SASL PLAIN.
+ */
+ class SaslSCRAMSHA1ClientConversation : public SaslClientConversation {
+ MONGO_DISALLOW_COPYING(SaslSCRAMSHA1ClientConversation);
+ public:
+ /**
+ * Implements the client side of a SASL PLAIN mechanism session.
+ **/
+ explicit SaslSCRAMSHA1ClientConversation(SaslClientSession* saslClientSession);
+
+ virtual ~SaslSCRAMSHA1ClientConversation();
+
+ /**
+ * Takes one step in a SCRAM-SHA-1 conversation.
+ *
+ * @return !Status::OK() for failure. The boolean part indicates if the
+ * authentication conversation is finished or not.
+ *
+ **/
+ virtual StatusWith<bool> step(const StringData& inputData, std::string* outputData);
+
+ private:
+ /**
+ * Generates client-first-message.
+ **/
+ StatusWith<bool> _firstStep(std::string* outputData);
+
+ /**
+ * Parses server-first-message and generate client-final-message.
+ **/
+ StatusWith<bool> _secondStep(const std::vector<string>& input, std::string* outputData);
+
+ /**
+ * Generates client-first-message.
+ **/
+ StatusWith<bool> _thirdStep(const std::vector<string>& input, std::string* outputData);
+
+ int _step;
+ std::string _authMessage;
+ unsigned char _saltedPassword[scram::hashSize];
+
+ // client and server nonce concatenated
+ std::string _clientNonce;
+ };
+
+} // namespace mongo