diff options
Diffstat (limited to 'src/mongo/client/sasl_client_authenticate_impl.cpp')
| -rw-r--r-- | src/mongo/client/sasl_client_authenticate_impl.cpp | 189 |
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; |
