diff options
author | Andy Schwerin <schwerin@10gen.com> | 2013-01-31 17:59:23 -0500 |
---|---|---|
committer | Andy Schwerin <schwerin@10gen.com> | 2013-02-12 15:30:24 -0500 |
commit | 1b2b144a8829296cfb1a8f78864eebfe752d9cb3 (patch) | |
tree | 9e364eaa806faee5d8f86c051274e1eb51f6f05c /src/mongo/client/sasl_client_authenticate.cpp | |
parent | 00c53fbf50e539ca8a70f6b879cff9c1391fa603 (diff) | |
download | mongo-1b2b144a8829296cfb1a8f78864eebfe752d9cb3.tar.gz |
SERVER-8414/SERVER-8429 Support SASL authentication in DBClient::auth() method, tools.
Introduces a new overload of the auth() method of DBClientWithCommands to the
C++ driver. The new overload takes a single BSONObj of authentication
parameters as input, and is analagous to the saslClientAuthenticate method.
This new method supports all SASL methods, if the client has GSASL support and
previously ran the mongo initializers, plus it supports MONGO_CR. It is the
preferred entry point for authentication in the C++ client, going forward.
The old ::auth() method remains and keeps its existing behavior.
Diffstat (limited to 'src/mongo/client/sasl_client_authenticate.cpp')
-rw-r--r-- | src/mongo/client/sasl_client_authenticate.cpp | 173 |
1 files changed, 5 insertions, 168 deletions
diff --git a/src/mongo/client/sasl_client_authenticate.cpp b/src/mongo/client/sasl_client_authenticate.cpp index 74097ecb9da..c267ca9cbe0 100644 --- a/src/mongo/client/sasl_client_authenticate.cpp +++ b/src/mongo/client/sasl_client_authenticate.cpp @@ -20,15 +20,16 @@ #include "mongo/base/string_data.h" #include "mongo/bson/util/bson_extract.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" namespace mongo { using namespace mongoutils; + Status (*saslClientAuthenticate)(DBClientWithCommands* client, + const BSONObj& saslParameters, + void* sessionHook) = NULL; + const char* const saslStartCommandName = "saslStart"; const char* const saslContinueCommandName = "saslContinue"; const char* const saslCommandAutoAuthorizeFieldName = "autoAuthorize"; @@ -44,16 +45,10 @@ namespace mongo { const char* const saslCommandPrincipalSourceFieldName = "userSource"; const char* const saslCommandServiceHostnameFieldName = "serviceHostname"; const char* const saslCommandServiceNameFieldName = "serviceName"; + const char* const saslCommandDigestPasswordFieldName = "digestPassword"; const char* const saslDefaultDBName = "$sasl"; const char* const saslDefaultServiceName = "mongodb"; - const char* const saslClientLogFieldName = "clientLogLevel"; - -namespace { - // Default log level on the client for SASL log messages. - const int defaultSaslClientLogLevel = 4; -} // namespace - Status saslExtractPayload(const BSONObj& cmdObj, std::string* payload, BSONType* type) { BSONElement payloadElement; Status status = bsonExtractField(cmdObj, saslCommandPayloadFieldName, &payloadElement); @@ -84,162 +79,4 @@ namespace { return Status::OK(); } - -namespace { - - /** - * 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. - */ - Status configureSession(Gsasl* gsasl, - DBClientWithCommands* client, - const BSONObj& saslParameters, - void* sessionHook, - GsaslSession* session) { - - std::string mechanism; - Status status = bsonExtractStringField(saslParameters, - saslCommandMechanismFieldName, - &mechanism); - if (!status.isOK()) - return status; - - status = session->initializeClientSession(gsasl, mechanism, sessionHook); - if (!status.isOK()) - return status; - - std::string service; - status = bsonExtractStringFieldWithDefault(saslParameters, - saslCommandServiceNameFieldName, - saslDefaultServiceName, - &service); - if (!status.isOK()) - return status; - session->setProperty(GSASL_SERVICE, service); - - std::string hostname; - status = bsonExtractStringFieldWithDefault(saslParameters, - saslCommandServiceHostnameFieldName, - HostAndPort(client->getServerAddress()).host(), - &hostname); - 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); - } - - BSONElement passwordElement = saslParameters[saslCommandPasswordFieldName]; - if (passwordElement.type() == String) { - std::string passwordHash = client->createPasswordDigest(principalElement.str(), - passwordElement.str()); - session->setProperty(GSASL_PASSWORD, passwordHash); - } - else if (!passwordElement.eoo()) { - return Status(ErrorCodes::TypeMismatch, - str::stream() << "Expected string for " << passwordElement); - } - - return Status::OK(); - } - - 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; - } - -} // namespace - - Status saslClientAuthenticate(Gsasl *gsasl, - DBClientWithCommands* client, - const BSONObj& saslParameters, - void* sessionHook) { - - GsaslSession session; - - int saslLogLevel = getSaslClientLogLevel(saslParameters); - - Status status = configureSession(gsasl, client, saslParameters, sessionHook, &session); - if (!status.isOK()) - return status; - - std::string targetDatabase; - status = bsonExtractStringFieldWithDefault(saslParameters, - saslCommandPrincipalSourceFieldName, - saslDefaultDBName, - &targetDatabase); - if (!status.isOK()) - return status; - - BSONObj saslFirstCommandPrefix = BSON( - saslStartCommandName << 1 << - saslCommandMechanismFieldName << session.getMechanism()); - - BSONObj saslFollowupCommandPrefix = BSON(saslContinueCommandName << 1); - BSONObj saslCommandPrefix = saslFirstCommandPrefix; - BSONObj inputObj = BSON(saslCommandPayloadFieldName << ""); - bool isServerDone = false; - while (!session.isDone()) { - std::string payload; - BSONType type; - - status = saslExtractPayload(inputObj, &payload, &type); - if (!status.isOK()) - return status; - - LOG(saslLogLevel) << "sasl client input: " << base64::encode(payload) << endl; - - std::string responsePayload; - status = session.step(payload, &responsePayload); - if (!status.isOK()) - return status; - - LOG(saslLogLevel) << "sasl client output: " << base64::encode(responsePayload) << endl; - - BSONObjBuilder commandBuilder; - commandBuilder.appendElements(saslCommandPrefix); - commandBuilder.appendBinData(saslCommandPayloadFieldName, - int(responsePayload.size()), - BinDataGeneral, - responsePayload.c_str()); - BSONElement conversationId = inputObj[saslCommandConversationIdFieldName]; - if (!conversationId.eoo()) - commandBuilder.append(conversationId); - - // Server versions 2.3.2 and earlier may return "ok: 1" with a non-zero "code" field, - // indicating a failure. Subsequent versions should return "ok: 0" on failure with a - // non-zero "code" field to indicate specific failure. In all versions, ok: 1, code: >0 - // and ok: 0, code optional, indicate failure. - bool ok = client->runCommand(targetDatabase, commandBuilder.obj(), inputObj); - ErrorCodes::Error code = ErrorCodes::fromInt( - inputObj[saslCommandCodeFieldName].numberInt()); - - if (!ok || code != ErrorCodes::OK) { - if (code == ErrorCodes::OK) - code = ErrorCodes::UnknownError; - - return Status(code, inputObj[saslCommandErrmsgFieldName].str()); - } - - isServerDone = inputObj[saslCommandDoneFieldName].trueValue(); - saslCommandPrefix = saslFollowupCommandPrefix; - } - - if (!isServerDone) - return Status(ErrorCodes::ProtocolError, "Client finished before server."); - return Status::OK(); - } - } // namespace mongo |