summaryrefslogtreecommitdiff
path: root/src/mongo/client/sasl_client_authenticate_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/client/sasl_client_authenticate_impl.cpp')
-rw-r--r--src/mongo/client/sasl_client_authenticate_impl.cpp189
1 files changed, 103 insertions, 86 deletions
diff --git a/src/mongo/client/sasl_client_authenticate_impl.cpp b/src/mongo/client/sasl_client_authenticate_impl.cpp
index b7aaa218d10..30ee4bc9b40 100644
--- a/src/mongo/client/sasl_client_authenticate_impl.cpp
+++ b/src/mongo/client/sasl_client_authenticate_impl.cpp
@@ -13,6 +13,14 @@
* limitations under the License.
*/
+/**
+ * This module implements the client side of SASL authentication in MongoDB, in terms of the Cyrus
+ * SASL library. See <sasl/sasl.h> and http://cyrusimap.web.cmu.edu/ for relevant documentation.
+ *
+ * The primary entry point at runtime is saslClientAuthenticateImpl().
+ */
+
+#include <boost/scoped_ptr.hpp>
#include <string>
#include "mongo/base/init.h"
@@ -20,15 +28,13 @@
#include "mongo/base/string_data.h"
#include "mongo/bson/util/bson_extract.h"
#include "mongo/client/sasl_client_authenticate.h"
+#include "mongo/client/sasl_client_session.h"
#include "mongo/platform/cstdint.h"
#include "mongo/util/base64.h"
-#include "mongo/util/gsasl_session.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/net/hostandport.h"
-#include <gsasl.h> // Must be included after "mongo/platform/cstdint.h" because of SERVER-8086.
-
namespace mongo {
namespace {
@@ -37,134 +43,145 @@ namespace {
const char* const saslClientLogFieldName = "clientLogLevel";
- Gsasl* _gsaslLibraryContext = NULL;
+ int getSaslClientLogLevel(const BSONObj& saslParameters) {
+ int saslLogLevel = defaultSaslClientLogLevel;
+ BSONElement saslLogElement = saslParameters[saslClientLogFieldName];
+ if (saslLogElement.trueValue())
+ saslLogLevel = 1;
+ if (saslLogElement.isNumber())
+ saslLogLevel = saslLogElement.numberInt();
+ return saslLogLevel;
+ }
+
+ /**
+ * Gets the password data from "saslParameters" and stores it to "outPassword".
+ *
+ * If "saslParameters" indicates that the password needs to be "digested" via
+ * DBClientWithCommands::createPasswordDigest(), this method takes care of that.
+ * On success, the value of "*outPassword" is always the correct value to set
+ * as the password on the SaslClientSession.
+ *
+ * Returns Status::OK() on success, and ErrorCodes::NoSuchKey if the password data is not
+ * present in "saslParameters". Other ErrorCodes returned indicate other errors.
+ */
+ Status extractPassword(DBClientWithCommands* client,
+ const BSONObj& saslParameters,
+ std::string* outPassword) {
- MONGO_INITIALIZER(SaslClientContext)(InitializerContext* context) {
- fassert(16710, _gsaslLibraryContext == NULL);
+ std::string rawPassword;
+ Status status = bsonExtractStringField(saslParameters,
+ saslCommandPasswordFieldName,
+ &rawPassword);
+ if (!status.isOK())
+ return status;
+
+ bool digest;
+ status = bsonExtractBooleanFieldWithDefault(saslParameters,
+ saslCommandDigestPasswordFieldName,
+ true,
+ &digest);
+ if (!status.isOK())
+ return status;
- if (!gsasl_check_version(GSASL_VERSION))
- return Status(ErrorCodes::UnknownError, "Incompatible gsasl library.");
+ if (digest) {
+ std::string user;
+ status = bsonExtractStringField(saslParameters,
+ saslCommandPrincipalFieldName,
+ &user);
+ if (!status.isOK())
+ return status;
- int rc = gsasl_init(&_gsaslLibraryContext);
- if (GSASL_OK != rc)
- return Status(ErrorCodes::UnknownError, gsasl_strerror(rc));
+ *outPassword = client->createPasswordDigest(user, rawPassword);
+ }
+ else {
+ *outPassword = rawPassword;
+ }
return Status::OK();
}
/**
- * Configure "*session" as a client gsasl session for authenticating on the connection
- * "*client", with the given "saslParameters". "gsasl" and "sessionHook" are passed through
- * to GsaslSession::initializeClientSession, where they are documented.
+ * Configures "session" to perform the client side of a SASL conversation over connection
+ * "client".
+ *
+ * "saslParameters" is a BSON document providing the necessary configuration information.
+ *
+ * Returns Status::OK() on success.
*/
- Status configureSession(Gsasl* gsasl,
+ Status configureSession(SaslClientSession* session,
DBClientWithCommands* client,
- const BSONObj& saslParameters,
- void* sessionHook,
- GsaslSession* session) {
+ const BSONObj& saslParameters) {
- std::string mechanism;
+ std::string value;
Status status = bsonExtractStringField(saslParameters,
saslCommandMechanismFieldName,
- &mechanism);
+ &value);
if (!status.isOK())
return status;
+ session->setParameter(SaslClientSession::parameterMechanism, value);
- status = session->initializeClientSession(gsasl, mechanism, sessionHook);
- if (!status.isOK())
- return status;
-
- std::string service;
status = bsonExtractStringFieldWithDefault(saslParameters,
saslCommandServiceNameFieldName,
saslDefaultServiceName,
- &service);
+ &value);
if (!status.isOK())
return status;
- session->setProperty(GSASL_SERVICE, service);
+ session->setParameter(SaslClientSession::parameterServiceName, value);
- std::string hostname;
status = bsonExtractStringFieldWithDefault(saslParameters,
saslCommandServiceHostnameFieldName,
HostAndPort(client->getServerAddress()).host(),
- &hostname);
+ &value);
if (!status.isOK())
return status;
- session->setProperty(GSASL_HOSTNAME, hostname);
-
- BSONElement principalElement = saslParameters[saslCommandPrincipalFieldName];
- if (principalElement.type() == String) {
- session->setProperty(GSASL_AUTHID, principalElement.str());
- }
- else if (!principalElement.eoo()) {
- return Status(ErrorCodes::TypeMismatch,
- str::stream() << "Expected string for " << principalElement);
- }
+ session->setParameter(SaslClientSession::parameterServiceHostname, value);
- BSONElement passwordElement = saslParameters[saslCommandPasswordFieldName];
- if (passwordElement.type() == String) {
- bool digest;
- status = bsonExtractBooleanFieldWithDefault(saslParameters,
- saslCommandDigestPasswordFieldName,
- true,
- &digest);
- if (!status.isOK())
- return status;
+ status = bsonExtractStringField(saslParameters,
+ saslCommandPrincipalFieldName,
+ &value);
+ if (!status.isOK())
+ return status;
+ session->setParameter(SaslClientSession::parameterUser, value);
- std::string passwordHash;
- if (digest) {
- passwordHash = client->createPasswordDigest(principalElement.str(),
- passwordElement.str());
- }
- else {
- passwordHash = passwordElement.str();
- }
- session->setProperty(GSASL_PASSWORD, passwordHash);
+ status = extractPassword(client, saslParameters, &value);
+ if (status.isOK()) {
+ session->setParameter(SaslClientSession::parameterPassword, value);
}
- else if (!passwordElement.eoo()) {
- return Status(ErrorCodes::TypeMismatch,
- str::stream() << "Expected string for " << passwordElement);
+ else if (status != ErrorCodes::NoSuchKey) {
+ return status;
}
- return Status::OK();
+ return session->initialize();
}
- int getSaslClientLogLevel(const BSONObj& saslParameters) {
- int saslLogLevel = defaultSaslClientLogLevel;
- BSONElement saslLogElement = saslParameters[saslClientLogFieldName];
- if (saslLogElement.trueValue())
- saslLogLevel = 1;
- if (saslLogElement.isNumber())
- saslLogLevel = saslLogElement.numberInt();
- return saslLogLevel;
- }
-
- Status saslClientAuthenticateImpl(DBClientWithCommands* client,
- const BSONObj& saslParameters,
- void* sessionHook) {
-
- GsaslSession session;
+ /**
+ * Driver for the client side of a sasl authentication session, conducted synchronously over
+ * "client".
+ */
+ Status saslClientAuthenticateImpl(DBClientWithCommands* client, const BSONObj& saslParameters) {
int saslLogLevel = getSaslClientLogLevel(saslParameters);
- Status status = configureSession(_gsaslLibraryContext,
- client,
- saslParameters,
- sessionHook,
- &session);
+ SaslClientSession session;
+ Status status = configureSession(&session, client, saslParameters);
if (!status.isOK())
return status;
std::string targetDatabase;
- status = bsonExtractStringFieldWithDefault(saslParameters,
- saslCommandPrincipalSourceFieldName,
- saslDefaultDBName,
- &targetDatabase);
+ try {
+ status = bsonExtractStringFieldWithDefault(saslParameters,
+ saslCommandPrincipalSourceFieldName,
+ saslDefaultDBName,
+ &targetDatabase);
+ } catch (const DBException& ex) {
+ return ex.toStatus();
+ }
if (!status.isOK())
return status;
BSONObj saslFirstCommandPrefix = BSON(
saslStartCommandName << 1 <<
- saslCommandMechanismFieldName << session.getMechanism());
+ saslCommandMechanismFieldName <<
+ session.getParameter(SaslClientSession::parameterMechanism));
BSONObj saslFollowupCommandPrefix = BSON(saslContinueCommandName << 1);
BSONObj saslCommandPrefix = saslFirstCommandPrefix;