mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-30 17:10:48 +01:00
Client and common support for SASL authentication.
SERVER-7130, SERVER-7131, SERVER-7133
This commit is contained in:
parent
444675bbd5
commit
924afa4629
@ -200,6 +200,8 @@ add_option( "gcov" , "compile with flags for gcov" , 0 , True )
|
||||
add_option("smokedbprefix", "prefix to dbpath et al. for smoke tests", 1 , False )
|
||||
add_option("smokeauth", "run smoke tests with --auth", 0 , False )
|
||||
|
||||
add_option("use-sasl-client", "Support SASL authentication in the client library", 0, False)
|
||||
|
||||
add_option( "use-system-tcmalloc", "use system version of tcmalloc library", 0, True )
|
||||
|
||||
add_option( "use-system-pcre", "use system version of pcre library", 0, True )
|
||||
@ -819,6 +821,12 @@ def doConfigure(myenv):
|
||||
if not conf.CheckLib( v8_lib_choices ):
|
||||
Exit(1)
|
||||
|
||||
env['MONGO_BUILD_SASL_CLIENT'] = bool(has_option("use-sasl-client"))
|
||||
if env['MONGO_BUILD_SASL_CLIENT'] and not conf.CheckLibWithHeader(
|
||||
"gsasl", "gsasl.h", "C", "gsasl_check_version(GSASL_VERSION);", autoadd=False):
|
||||
|
||||
Exit(1)
|
||||
|
||||
# requires ports devel/libexecinfo to be installed
|
||||
if freebsd or openbsd:
|
||||
if not conf.CheckLib("execinfo"):
|
||||
|
@ -126,6 +126,13 @@ commonFiles = [ "pch.cpp",
|
||||
"db/dbmessage.cpp"
|
||||
]
|
||||
|
||||
commonSysLibdeps = []
|
||||
|
||||
if env['MONGO_BUILD_SASL_CLIENT']:
|
||||
commonFiles.extend(['client/sasl_client_authenticate.cpp',
|
||||
'util/gsasl_session.cpp'])
|
||||
commonSysLibdeps.append('gsasl')
|
||||
|
||||
# handle processinfo*
|
||||
processInfoFiles = [ "util/processinfo.cpp" ]
|
||||
|
||||
@ -161,7 +168,8 @@ env.StaticLibrary('mongocommon', commonFiles,
|
||||
'fail_point',
|
||||
'$BUILD_DIR/third_party/pcrecpp',
|
||||
'$BUILD_DIR/third_party/murmurhash3/murmurhash3',
|
||||
'$BUILD_DIR/third_party/shim_boost'],)
|
||||
'$BUILD_DIR/third_party/shim_boost'],
|
||||
SYSLIBDEPS=commonSysLibdeps)
|
||||
|
||||
env.StaticLibrary("coredb", [
|
||||
"client/parallel.cpp",
|
||||
|
@ -16,7 +16,11 @@ error_code("UnsupportedFormat", 12)
|
||||
error_code("Unauthorized", 13)
|
||||
error_code("TypeMismatch", 14)
|
||||
error_code("Overflow", 15)
|
||||
error_code("IllegalOperation", 16)
|
||||
error_code("EmptyArrayOperation", 17)
|
||||
error_code("InvalidLength", 16)
|
||||
error_code("ProtocolError", 17)
|
||||
error_code("AuthenticationFailed", 18)
|
||||
error_code("CannotReuseObject", 19)
|
||||
error_code("IllegalOperation", 20)
|
||||
error_code("EmptyArrayOperation", 21)
|
||||
|
||||
error_class("NetworkError", ["HostUnreachable", "HostNotFound"])
|
||||
|
229
src/mongo/client/sasl_client_authenticate.cpp
Normal file
229
src/mongo/client/sasl_client_authenticate.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
/* Copyright 2012 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "mongo/client/sasl_client_authenticate.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#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;
|
||||
|
||||
const char* const saslStartCommandName = "saslStart";
|
||||
const char* const saslContinueCommandName = "saslContinue";
|
||||
const char* const saslCommandAutoAuthorizeFieldName = "autoAuthorize";
|
||||
const char* const saslCommandCodeFieldName = "code";
|
||||
const char* const saslCommandConversationIdFieldName = "conversationId";
|
||||
const char* const saslCommandDoneFieldName = "done";
|
||||
const char* const saslCommandErrmsgFieldName = "errmsg";
|
||||
const char* const saslCommandMechanismFieldName = "mechanism";
|
||||
const char* const saslCommandMechanismListFieldName = "supportedMechanisms";
|
||||
const char* const saslCommandPasswordFieldName = "password";
|
||||
const char* const saslCommandPayloadFieldName = "payload";
|
||||
const char* const saslCommandPrincipalFieldName = "principal";
|
||||
const char* const saslCommandServiceHostnameFieldName = "serviceHostname";
|
||||
const char* const saslCommandServiceNameFieldName = "serviceName";
|
||||
const char* const saslDefaultDBName = "admin";
|
||||
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);
|
||||
if (!status.isOK())
|
||||
return status;
|
||||
|
||||
*type = payloadElement.type();
|
||||
if (payloadElement.type() == BinData) {
|
||||
const char* payloadData;
|
||||
int payloadLen;
|
||||
payloadData = payloadElement.binData(payloadLen);
|
||||
if (payloadLen < 0)
|
||||
return Status(ErrorCodes::InvalidLength, "Negative payload length");
|
||||
*payload = std::string(payloadData, payloadData + payloadLen);
|
||||
}
|
||||
else if (payloadElement.type() == String) {
|
||||
try {
|
||||
*payload = base64::decode(payloadElement.str());
|
||||
} catch (UserException& e) {
|
||||
return Status(ErrorCodes::FailedToParse, e.what());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return Status(ErrorCodes::TypeMismatch,
|
||||
(str::stream() << "Wrong type for field; expected BinData or String for "
|
||||
<< payloadElement));
|
||||
}
|
||||
|
||||
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 element = saslParameters[saslCommandPrincipalFieldName];
|
||||
if (element.type() == String) {
|
||||
session->setProperty(GSASL_AUTHID, element.str());
|
||||
}
|
||||
else if (!element.eoo()) {
|
||||
return Status(ErrorCodes::TypeMismatch,
|
||||
str::stream() << "Expected string for " << element);
|
||||
}
|
||||
|
||||
element = saslParameters[saslCommandPasswordFieldName];
|
||||
if (element.type() == String) {
|
||||
session->setProperty(GSASL_PASSWORD, element.str());
|
||||
}
|
||||
else if (!element.eoo()) {
|
||||
return Status(ErrorCodes::TypeMismatch,
|
||||
str::stream() << "Expected string for " << element);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
if (!client->runCommand(saslDefaultDBName, commandBuilder.obj(), inputObj)) {
|
||||
return Status(ErrorCodes::UnknownError,
|
||||
inputObj[saslCommandErrmsgFieldName].str());
|
||||
}
|
||||
|
||||
int statusCodeInt = inputObj[saslCommandCodeFieldName].Int();
|
||||
if (0 != statusCodeInt)
|
||||
return Status(ErrorCodes::fromInt(statusCodeInt),
|
||||
inputObj[saslCommandErrmsgFieldName].str());
|
||||
|
||||
isServerDone = inputObj[saslCommandDoneFieldName].trueValue();
|
||||
saslCommandPrefix = saslFollowupCommandPrefix;
|
||||
}
|
||||
|
||||
if (!isServerDone)
|
||||
return Status(ErrorCodes::ProtocolError, "Client finished before server.");
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace mongo
|
129
src/mongo/client/sasl_client_authenticate.h
Normal file
129
src/mongo/client/sasl_client_authenticate.h
Normal file
@ -0,0 +1,129 @@
|
||||
/* Copyright 2012 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/base/status.h"
|
||||
#include "mongo/bson/bsontypes.h"
|
||||
#include "mongo/client/dbclientinterface.h"
|
||||
|
||||
struct Gsasl;
|
||||
|
||||
namespace mongo {
|
||||
class BSONObj;
|
||||
|
||||
/**
|
||||
* Attempts to authenticate "client" using the SASL protocol.
|
||||
*
|
||||
* Requires an initialized instance of the "gsasl" library, as the first
|
||||
* parameter.
|
||||
*
|
||||
* The "saslParameters" BSONObj should be initialized with zero or more of the
|
||||
* fields below. Which fields are required depends on the mechanism. Consult the
|
||||
* libgsasl documentation.
|
||||
*
|
||||
* "mechanism": The string name of the sasl mechanism to use. Mandatory.
|
||||
* "autoAuthorize": Truthy values tell the server to automatically acquire privileges on
|
||||
* all resources after successful authentication, which is the default. Falsey values
|
||||
* instruct the server to await separate privilege-acquisition commands.
|
||||
* "database": The database target of the auth command. Optional for GSSAPI/Kerberos.
|
||||
* "principal": The string name of the principal to authenticate, GSASL_AUTHID.
|
||||
* "password": The password data, GSASL_PASSWORD.
|
||||
* "serviceName": The GSSAPI service name to use. Defaults to "mongodb".
|
||||
* "serviceHostname": The GSSAPI hostname to use. Defaults to the name of the remote host.
|
||||
*
|
||||
* Other fields in saslParameters are silently ignored.
|
||||
*
|
||||
* "sessionHook" is a pointer to optional data, which may be used by the gsasl_callback
|
||||
* previously set on "gsasl". The session hook is set on an underlying Gsasl_session using
|
||||
* gsasl_session_hook_set, and may be accessed by callbacks using gsasl_session_hook_get.
|
||||
* See the gsasl documentation.
|
||||
*
|
||||
* Returns an OK status on success, and ErrorCodes::AuthenticationFailed if authentication is
|
||||
* rejected. Other failures, all of which are tantamount to authentication failure, may also be
|
||||
* returned.
|
||||
*/
|
||||
Status saslClientAuthenticate(Gsasl *gsasl,
|
||||
DBClientWithCommands* client,
|
||||
const BSONObj& saslParameters,
|
||||
void* sessionHook);
|
||||
|
||||
/**
|
||||
* Extracts the payload field from "cmdObj", and store it into "*payload".
|
||||
*
|
||||
* Sets "*type" to the BSONType of the payload field in cmdObj.
|
||||
*
|
||||
* If the type of the payload field is String, the contents base64 decodes and
|
||||
* stores into "*payload". If the type is BinData, the contents are stored directly
|
||||
* into "*payload". In all other cases, returns
|
||||
*/
|
||||
Status saslExtractPayload(const BSONObj& cmdObj, std::string* payload, BSONType* type);
|
||||
|
||||
// Constants
|
||||
|
||||
/// String name of the saslStart command.
|
||||
extern const char* const saslStartCommandName;
|
||||
|
||||
/// String name of the saslContinue command.
|
||||
extern const char* const saslContinueCommandName;
|
||||
|
||||
/// Name of the saslStart parameter indicating that the server should automatically grant the
|
||||
/// connection all privileges associated with the principal after successful authentication.
|
||||
extern const char* const saslCommandAutoAuthorizeFieldName;
|
||||
|
||||
/// Name of the field contain the status code in responses from the server.
|
||||
extern const char* const saslCommandCodeFieldName;
|
||||
|
||||
/// Name of the field containing the conversation identifier in server respones and saslContinue
|
||||
/// commands.
|
||||
extern const char* const saslCommandConversationIdFieldName;
|
||||
|
||||
/// Name of the field that indicates whether or not the server believes authentication has
|
||||
/// completed successfully.
|
||||
extern const char* const saslCommandDoneFieldName;
|
||||
|
||||
/// Field in which to store error messages associated with non-success return codes.
|
||||
extern const char* const saslCommandErrmsgFieldName;
|
||||
|
||||
/// Name of parameter to saslStart command indiciating the client's desired sasl mechanism.
|
||||
extern const char* const saslCommandMechanismFieldName;
|
||||
|
||||
/// In the event that saslStart supplies an unsupported mechanism, the server responds with a
|
||||
/// field by this name, with a list of supported mechanisms.
|
||||
extern const char* const saslCommandMechanismListFieldName;
|
||||
|
||||
/// Field containing password information for saslClientAuthenticate().
|
||||
extern const char* const saslCommandPasswordFieldName;
|
||||
|
||||
/// Field containing sasl payloads passed to and from the server.
|
||||
extern const char* const saslCommandPayloadFieldName;
|
||||
|
||||
/// Field containing the string identifier of the principal to authenticate in
|
||||
/// saslClientAuthenticate().
|
||||
extern const char* const saslCommandPrincipalFieldName;
|
||||
|
||||
/// Field overriding the FQDN of the hostname hosting the mongodb srevice in
|
||||
/// saslClientAuthenticate().
|
||||
extern const char* const saslCommandServiceHostnameFieldName;
|
||||
|
||||
/// Field overriding the name of the mongodb service saslClientAuthenticate().
|
||||
extern const char* const saslCommandServiceNameFieldName;
|
||||
|
||||
/// Default database against which sasl authentication commands should run.
|
||||
extern const char* const saslDefaultDBName;
|
||||
|
||||
/// Default sasl service name, "mongodb".
|
||||
extern const char* const saslDefaultServiceName;
|
||||
}
|
94
src/mongo/util/gsasl_session.cpp
Normal file
94
src/mongo/util/gsasl_session.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
/* Copyright 2012 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "mongo/util/gsasl_session.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <gsasl.h>
|
||||
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
GsaslSession::GsaslSession() : _gsaslSession(NULL), _done(false) {}
|
||||
|
||||
GsaslSession::~GsaslSession() {
|
||||
if (_gsaslSession)
|
||||
gsasl_finish(_gsaslSession);
|
||||
}
|
||||
|
||||
std::string GsaslSession::getMechanism() const {
|
||||
return gsasl_mechanism_name(_gsaslSession);
|
||||
}
|
||||
|
||||
void GsaslSession::setProperty(Gsasl_property property, const StringData& value) {
|
||||
gsasl_property_set_raw(_gsaslSession, property, value.data(), value.size());
|
||||
}
|
||||
|
||||
Status GsaslSession::initializeClientSession(Gsasl* gsasl,
|
||||
const StringData& mechanism,
|
||||
void* sessionHook) {
|
||||
return _initializeSession(&gsasl_client_start, gsasl, mechanism, sessionHook);
|
||||
}
|
||||
|
||||
Status GsaslSession::initializeServerSession(Gsasl* gsasl,
|
||||
const StringData& mechanism,
|
||||
void* sessionHook) {
|
||||
return _initializeSession(&gsasl_server_start, gsasl, mechanism, sessionHook);
|
||||
}
|
||||
|
||||
Status GsaslSession::_initializeSession(
|
||||
GsaslSessionStartFn sessionStartFn,
|
||||
Gsasl* gsasl, const StringData& mechanism, void* sessionHook) {
|
||||
|
||||
if (_done || _gsaslSession)
|
||||
return Status(ErrorCodes::CannotReuseObject, "Cannot reuse GsaslSession.");
|
||||
|
||||
int rc = sessionStartFn(gsasl, mechanism.data(), &_gsaslSession);
|
||||
switch (rc) {
|
||||
case GSASL_OK:
|
||||
gsasl_session_hook_set(_gsaslSession, sessionHook);
|
||||
return Status::OK();
|
||||
case GSASL_UNKNOWN_MECHANISM:
|
||||
return Status(ErrorCodes::BadValue, gsasl_strerror(rc));
|
||||
default:
|
||||
return Status(ErrorCodes::ProtocolError, gsasl_strerror(rc));
|
||||
}
|
||||
}
|
||||
|
||||
Status GsaslSession::step(const StringData& inputData, std::string* outputData) {
|
||||
char* output;
|
||||
size_t outputSize;
|
||||
int rc = gsasl_step(_gsaslSession,
|
||||
inputData.data(), inputData.size(),
|
||||
&output, &outputSize);
|
||||
|
||||
if (GSASL_OK == rc)
|
||||
_done = true;
|
||||
|
||||
switch (rc) {
|
||||
case GSASL_OK:
|
||||
case GSASL_NEEDS_MORE:
|
||||
*outputData = std::string(output, output + outputSize);
|
||||
free(output);
|
||||
return Status::OK();
|
||||
case GSASL_AUTHENTICATION_ERROR:
|
||||
return Status(ErrorCodes::AuthenticationFailed, gsasl_strerror(rc));
|
||||
default:
|
||||
return Status(ErrorCodes::ProtocolError, gsasl_strerror(rc));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mongo
|
138
src/mongo/util/gsasl_session.h
Normal file
138
src/mongo/util/gsasl_session.h
Normal file
@ -0,0 +1,138 @@
|
||||
/* Copyright 2012 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gsasl.h>
|
||||
#include <string>
|
||||
|
||||
#include "mongo/base/disallow_copying.h"
|
||||
#include "mongo/base/status.h"
|
||||
#include "mongo/base/string_data.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
/**
|
||||
* C++ wrapper around Gsasl_session.
|
||||
*/
|
||||
class GsaslSession {
|
||||
MONGO_DISALLOW_COPYING(GsaslSession);
|
||||
public:
|
||||
GsaslSession();
|
||||
~GsaslSession();
|
||||
|
||||
/**
|
||||
* Initializes "this" as a client sasl session.
|
||||
*
|
||||
* May only be called once on an instance of GsaslSession, and may not be called on an
|
||||
* instance on which initializeServerSession has been called.
|
||||
*
|
||||
* "gsasl" is a pointer to a Gsasl library context that will exist for the rest of
|
||||
* the lifetime of "this".
|
||||
*
|
||||
* "mechanism" is a SASL mechanism name.
|
||||
*
|
||||
* "sessionHook" is user-supplied data associated with this session. If is accessible in
|
||||
* the gsasl callback set on "gsasl" using gsasl_session_hook_get(). May be NULL. Owned
|
||||
* by caller, and must stay in scope as long as this object.
|
||||
*
|
||||
* Returns Status::OK() on success, some other status on errors.
|
||||
*/
|
||||
Status initializeClientSession(Gsasl* gsasl,
|
||||
const StringData& mechanism,
|
||||
void* sessionHook);
|
||||
|
||||
/**
|
||||
* Initializes "this" as a server sasl session.
|
||||
*
|
||||
* May only be called once on an instance of GsaslSession, and may not be called on an
|
||||
* instance on which initializeClientSession has been called.
|
||||
*
|
||||
* "gsasl" is a pointer to a Gsasl library context that will exist for the rest of
|
||||
* the lifetime of "this".
|
||||
*
|
||||
* "mechanism" is a SASL mechanism name.
|
||||
*
|
||||
* "sessionHook" is user-supplied data associated with this session. If is accessible in
|
||||
* the gsasl callback set on "gsasl" using gsasl_session_hook_get(). May be NULL. Owned
|
||||
* by caller, and must stay in scope as long as this object.
|
||||
*
|
||||
* Returns Status::OK() on success, some other status on errors.
|
||||
*/
|
||||
Status initializeServerSession(Gsasl* gsasl,
|
||||
const StringData& mechanism,
|
||||
void* sessionHook);
|
||||
|
||||
/**
|
||||
* Returns the string name of the SASL mechanism in use in this session.
|
||||
*
|
||||
* Not valid before initializeServerSession() or initializeClientSession().
|
||||
*/
|
||||
std::string getMechanism() const;
|
||||
|
||||
/**
|
||||
* Sets a property on this session.
|
||||
*
|
||||
* Not valid before initializeServerSession() or initializeClientSession().
|
||||
*/
|
||||
void setProperty(Gsasl_property property, const StringData& value);
|
||||
|
||||
/**
|
||||
* Performs one more step on this session.
|
||||
*
|
||||
* Receives "inputData" from the other side and produces "*outputData" to send.
|
||||
*
|
||||
* Both "inputData" and "*outputData" are logically strings of bytes, not characters.
|
||||
*
|
||||
* For the first step by the authentication initiator, "inputData" should have 0 length.
|
||||
*
|
||||
* Returns Status::OK() on success. In that case, isDone() can be queried to see if the
|
||||
* session expects another call to step(). If isDone() is true, the authentication has
|
||||
* completed successfully.
|
||||
*
|
||||
* Any return other than Status::OK() means that authentication has failed, but the specific
|
||||
* code or reason message may provide insight as to why.
|
||||
*/
|
||||
Status step(const StringData& inputData, std::string* outputData);
|
||||
|
||||
/**
|
||||
* Returns true if this session has completed successfully.
|
||||
*
|
||||
* That is, returns true if the session expects no more calls to step(), and all previous
|
||||
* calls to step() and initializeClientSession()/initializeServerSession() have returned
|
||||
* Status::OK().
|
||||
*/
|
||||
bool isDone() const { return _done; }
|
||||
|
||||
private:
|
||||
// Signature of gsas session start functions.
|
||||
typedef int (*GsaslSessionStartFn)(Gsasl*, const char*, Gsasl_session**);
|
||||
|
||||
/**
|
||||
* Common helper code for initializing a session.
|
||||
*
|
||||
* Uses "sessionStartFn" to initialize the underlying Gsasl_session.
|
||||
*/
|
||||
Status _initializeSession(GsaslSessionStartFn sessionStartFn,
|
||||
Gsasl* gsasl, const StringData& mechanism, void* sessionHook);
|
||||
|
||||
/// Underlying C-library gsasl session object.
|
||||
Gsasl_session* _gsaslSession;
|
||||
|
||||
/// See isDone(), above.
|
||||
bool _done;
|
||||
};
|
||||
|
||||
} // namespace mongo
|
Loading…
Reference in New Issue
Block a user