diff options
Diffstat (limited to 'src/mongo/client/sasl_client_authenticate_impl.cpp')
-rw-r--r-- | src/mongo/client/sasl_client_authenticate_impl.cpp | 366 |
1 files changed, 173 insertions, 193 deletions
diff --git a/src/mongo/client/sasl_client_authenticate_impl.cpp b/src/mongo/client/sasl_client_authenticate_impl.cpp index a2447e2c507..5cc5df37555 100644 --- a/src/mongo/client/sasl_client_authenticate_impl.cpp +++ b/src/mongo/client/sasl_client_authenticate_impl.cpp @@ -53,229 +53,209 @@ namespace mongo { - using std::endl; +using std::endl; namespace { - // Default log level on the client for SASL log messages. - const int defaultSaslClientLogLevel = 4; +// Default log level on the client for SASL log messages. +const int defaultSaslClientLogLevel = 4; - const char* const saslClientLogFieldName = "clientLogLevel"; +const char* const saslClientLogFieldName = "clientLogLevel"; - 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; - } +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 "digestPassword" indicates that the password needs to be "digested" via - * mongo::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(const BSONObj& saslParameters, - bool digestPassword, - std::string* outPassword) { - - std::string rawPassword; - Status status = bsonExtractStringField(saslParameters, - saslCommandPasswordFieldName, - &rawPassword); +/** + * Gets the password data from "saslParameters" and stores it to "outPassword". + * + * If "digestPassword" indicates that the password needs to be "digested" via + * mongo::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(const BSONObj& saslParameters, + bool digestPassword, + std::string* outPassword) { + std::string rawPassword; + Status status = + bsonExtractStringField(saslParameters, saslCommandPasswordFieldName, &rawPassword); + if (!status.isOK()) + return status; + + if (digestPassword) { + std::string user; + status = bsonExtractStringField(saslParameters, saslCommandUserFieldName, &user); if (!status.isOK()) return status; - if (digestPassword) { - std::string user; - status = bsonExtractStringField(saslParameters, - saslCommandUserFieldName, - &user); - if (!status.isOK()) - return status; + *outPassword = mongo::createPasswordDigest(user, rawPassword); + } else { + *outPassword = rawPassword; + } + return Status::OK(); +} - *outPassword = mongo::createPasswordDigest(user, rawPassword); - } - else { - *outPassword = rawPassword; - } - return Status::OK(); +/** + * 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(SaslClientSession* session, + DBClientWithCommands* client, + const std::string& targetDatabase, + const BSONObj& saslParameters) { + std::string mechanism; + Status status = + bsonExtractStringField(saslParameters, saslCommandMechanismFieldName, &mechanism); + if (!status.isOK()) + return status; + session->setParameter(SaslClientSession::parameterMechanism, mechanism); + + std::string value; + status = bsonExtractStringFieldWithDefault( + saslParameters, saslCommandServiceNameFieldName, saslDefaultServiceName, &value); + if (!status.isOK()) + return status; + session->setParameter(SaslClientSession::parameterServiceName, value); + + status = bsonExtractStringFieldWithDefault(saslParameters, + saslCommandServiceHostnameFieldName, + HostAndPort(client->getServerAddress()).host(), + &value); + if (!status.isOK()) + return status; + session->setParameter(SaslClientSession::parameterServiceHostname, value); + + status = bsonExtractStringField(saslParameters, saslCommandUserFieldName, &value); + if (!status.isOK()) + return status; + session->setParameter(SaslClientSession::parameterUser, value); + + bool digestPasswordDefault = !(targetDatabase == "$external" && mechanism == "PLAIN") && + !(targetDatabase == "$external" && mechanism == "GSSAPI"); + bool digestPassword; + status = bsonExtractBooleanFieldWithDefault( + saslParameters, saslCommandDigestPasswordFieldName, digestPasswordDefault, &digestPassword); + if (!status.isOK()) + return status; + + status = extractPassword(saslParameters, digestPassword, &value); + if (status.isOK()) { + session->setParameter(SaslClientSession::parameterPassword, value); + } else if (!(status == ErrorCodes::NoSuchKey && targetDatabase == "$external")) { + // $external users do not have passwords, hence NoSuchKey is expected + return status; } - /** - * 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(SaslClientSession* session, - DBClientWithCommands* client, - const std::string& targetDatabase, - const BSONObj& saslParameters) { - - std::string mechanism; - Status status = bsonExtractStringField(saslParameters, - saslCommandMechanismFieldName, - &mechanism); - if (!status.isOK()) - return status; - session->setParameter(SaslClientSession::parameterMechanism, mechanism); + return session->initialize(); +} - std::string value; - status = bsonExtractStringFieldWithDefault(saslParameters, - saslCommandServiceNameFieldName, - saslDefaultServiceName, - &value); - if (!status.isOK()) - return status; - session->setParameter(SaslClientSession::parameterServiceName, value); +/** + * 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 = bsonExtractStringFieldWithDefault(saslParameters, - saslCommandServiceHostnameFieldName, - HostAndPort(client->getServerAddress()).host(), - &value); + std::string targetDatabase; + try { + Status status = bsonExtractStringFieldWithDefault( + saslParameters, saslCommandUserDBFieldName, saslDefaultDBName, &targetDatabase); if (!status.isOK()) return status; - session->setParameter(SaslClientSession::parameterServiceHostname, value); + } catch (const DBException& ex) { + return ex.toStatus(); + } - status = bsonExtractStringField(saslParameters, - saslCommandUserFieldName, - &value); - if (!status.isOK()) - return status; - session->setParameter(SaslClientSession::parameterUser, value); - - bool digestPasswordDefault = - !(targetDatabase == "$external" && mechanism == "PLAIN") && - !(targetDatabase == "$external" && mechanism == "GSSAPI"); - bool digestPassword; - status = bsonExtractBooleanFieldWithDefault(saslParameters, - saslCommandDigestPasswordFieldName, - digestPasswordDefault, - &digestPassword); - if (!status.isOK()) - return status; + std::string mechanism; + Status status = + bsonExtractStringField(saslParameters, saslCommandMechanismFieldName, &mechanism); + if (!status.isOK()) { + return status; + } - status = extractPassword(saslParameters, digestPassword, &value); - if (status.isOK()) { - session->setParameter(SaslClientSession::parameterPassword, value); - } - else if (!(status == ErrorCodes::NoSuchKey && targetDatabase == "$external")) { - // $external users do not have passwords, hence NoSuchKey is expected - return status; - } + std::unique_ptr<SaslClientSession> session(SaslClientSession::create(mechanism)); + status = configureSession(session.get(), client, targetDatabase, saslParameters); - return session->initialize(); - } + if (!status.isOK()) + return status; - /** - * 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); - - std::string targetDatabase; - try { - Status status = bsonExtractStringFieldWithDefault(saslParameters, - saslCommandUserDBFieldName, - saslDefaultDBName, - &targetDatabase); - if (!status.isOK()) - return status; - } catch (const DBException& ex) { - return ex.toStatus(); - } + BSONObj saslFirstCommandPrefix = + BSON(saslStartCommandName << 1 << saslCommandMechanismFieldName + << session->getParameter(SaslClientSession::parameterMechanism)); - std::string mechanism; - Status status = bsonExtractStringField(saslParameters, - saslCommandMechanismFieldName, - &mechanism); - if(!status.isOK()) { + 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; - } - std::unique_ptr<SaslClientSession> session(SaslClientSession::create(mechanism)); - status = configureSession(session.get(), client, targetDatabase, saslParameters); - + LOG(saslLogLevel) << "sasl client input: " << base64::encode(payload) << endl; + + std::string responsePayload; + status = session->step(payload, &responsePayload); if (!status.isOK()) return status; - BSONObj saslFirstCommandPrefix = BSON( - saslStartCommandName << 1 << - saslCommandMechanismFieldName << - session->getParameter(SaslClientSession::parameterMechanism)); - - 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; + 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()); } - if (!isServerDone) - return Status(ErrorCodes::ProtocolError, "Client finished before server."); - return Status::OK(); + isServerDone = inputObj[saslCommandDoneFieldName].trueValue(); + saslCommandPrefix = saslFollowupCommandPrefix; } - MONGO_INITIALIZER(SaslClientAuthenticateFunction)(InitializerContext* context) { - saslClientAuthenticate = saslClientAuthenticateImpl; - return Status::OK(); - } + if (!isServerDone) + return Status(ErrorCodes::ProtocolError, "Client finished before server."); + return Status::OK(); +} + +MONGO_INITIALIZER(SaslClientAuthenticateFunction)(InitializerContext* context) { + saslClientAuthenticate = saslClientAuthenticateImpl; + return Status::OK(); +} } // namespace } // namespace mongo |