summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Nilsson <agralius@gmail.com>2014-08-29 13:10:29 -0400
committerAndreas Nilsson <agralius@gmail.com>2014-09-04 14:54:11 -0400
commitc14c1f113d56da4e32ac13a123028c916f0c8cff (patch)
tree28407824a59d40567902039ce5c14cdde1aa0d7a
parent86f017917aac53e1adfe991fc21a08416c84d990 (diff)
downloadmongo-c14c1f113d56da4e32ac13a123028c916f0c8cff.tar.gz
SERVER-7596 SASL client code refactor for SCRAM
-rw-r--r--src/mongo/SConscript11
-rw-r--r--src/mongo/client/cyrus_sasl_client_session.cpp314
-rw-r--r--src/mongo/client/cyrus_sasl_client_session.h85
-rw-r--r--src/mongo/client/native_sasl_client_session.cpp66
-rw-r--r--src/mongo/client/native_sasl_client_session.h57
-rw-r--r--src/mongo/client/sasl_client_authenticate_impl.cpp11
-rw-r--r--src/mongo/client/sasl_client_session.cpp290
-rw-r--r--src/mongo/client/sasl_client_session.h46
-rw-r--r--src/mongo/client/sasl_sspi.cpp6
-rw-r--r--src/mongo/db/auth/sasl_authentication_session.cpp3
-rw-r--r--src/mongo/db/auth/sasl_authentication_session.h4
11 files changed, 567 insertions, 326 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index c14c58d7eee..4e6d5e1642a 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -286,8 +286,11 @@ env.Library('clientdriver', [
"client/dbclient.cpp",
"client/dbclient_rs.cpp",
"client/dbclientcursor.cpp",
+ 'client/native_sasl_client_session.cpp',
"client/replica_set_monitor.cpp",
'client/sasl_client_authenticate.cpp',
+ "client/sasl_client_authenticate_impl.cpp",
+ 'client/sasl_client_session.cpp',
"client/syncclusterconnection.cpp",
"db/dbmessage.cpp"
],
@@ -336,16 +339,16 @@ if env['MONGO_BUILD_SASL_CLIENT']:
if env['PYSYSPLATFORM'] == "win32":
saslLibs.extend(["secur32"])
- env.Library('sasl_client_session',
- ['client/sasl_client_session.cpp',
+ env.Library('cyrus_sasl_client_session',
+ ['client/cyrus_sasl_client_session.cpp',
'client/sasl_sspi.cpp'],
LIBDEPS = [
+ 'clientdriver',
'foundation',
'signal_handlers_synchronous',
],
SYSLIBDEPS=saslLibs)
- commonFiles.extend(['client/sasl_client_authenticate_impl.cpp'])
- extraCommonLibdeps.append('sasl_client_session')
+ extraCommonLibdeps.append('cyrus_sasl_client_session')
# handle processinfo*
processInfoFiles = [ "util/processinfo.cpp" ]
diff --git a/src/mongo/client/cyrus_sasl_client_session.cpp b/src/mongo/client/cyrus_sasl_client_session.cpp
new file mode 100644
index 00000000000..1c7d92c1924
--- /dev/null
+++ b/src/mongo/client/cyrus_sasl_client_session.cpp
@@ -0,0 +1,314 @@
+/* Copyright 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/client/cyrus_sasl_client_session.h"
+
+#include "mongo/base/init.h"
+#include "mongo/util/allocator.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/concurrency/mutex.h"
+#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/signal_handlers_synchronous.h"
+
+namespace mongo {
+namespace {
+
+ SaslClientSession* createCyrusSaslClientSession() {
+ return new CyrusSaslClientSession();
+ }
+
+ /*
+ * Allocator functions to be used by the SASL library, if the client
+ * doesn't initialize the library for us.
+ */
+
+// Version 2.1.26 is the first version to use size_t in the allocator signatures
+#if (SASL_VERSION_FULL >= ((2 << 16) | (1 << 8) | 26))
+ typedef size_t SaslAllocSize;
+#else
+ typedef unsigned long SaslAllocSize;
+#endif
+
+ typedef int(*SaslCallbackFn)();
+
+ void* saslOurMalloc(SaslAllocSize sz) {
+ return mongoMalloc(sz);
+ }
+
+ void* saslOurCalloc(SaslAllocSize count, SaslAllocSize size) {
+ void* ptr = calloc(count, size);
+ if (!ptr) {
+ reportOutOfMemoryErrorAndExit();
+ }
+ return ptr;
+ }
+
+ void* saslOurRealloc(void* ptr, SaslAllocSize sz) {
+ return mongoRealloc(ptr, sz);
+ }
+
+ /*
+ * Mutex functions to be used by the SASL library, if the client doesn't initialize the library
+ * for us.
+ */
+
+ void* saslMutexAlloc(void) {
+ return new SimpleMutex("sasl");
+ }
+
+ int saslMutexLock(void* mutex) {
+ static_cast<SimpleMutex*>(mutex)->lock();
+ return SASL_OK;
+ }
+
+ int saslMutexUnlock(void* mutex) {
+ static_cast<SimpleMutex*>(mutex)->unlock();
+ return SASL_OK;
+ }
+
+ void saslMutexFree(void* mutex) {
+ delete static_cast<SimpleMutex*>(mutex);
+ }
+
+ /**
+ * Configures the SASL library to use allocator and mutex functions we specify,
+ * unless the client application has previously initialized the SASL library.
+ */
+ MONGO_INITIALIZER(CyrusSaslAllocatorsAndMutexes)(InitializerContext*) {
+ sasl_set_alloc(saslOurMalloc,
+ saslOurCalloc,
+ saslOurRealloc,
+ free);
+
+ sasl_set_mutex(saslMutexAlloc,
+ saslMutexLock,
+ saslMutexUnlock,
+ saslMutexFree);
+ return Status::OK();
+ }
+
+ int saslClientLogSwallow(void *context, int priority, const char *message) {
+ return SASL_OK; // do nothing
+ }
+
+ /**
+ * Initializes the client half of the SASL library, but is effectively a no-op if the client
+ * application has already done it.
+ *
+ * If a client wishes to override this initialization but keep the allocator and mutex
+ * initialization, it should implement a MONGO_INITIALIZER_GENERAL with
+ * CyrusSaslAllocatorsAndMutexes as a prerequisite and CyrusSaslClientContext as a
+ * dependent. If it wishes to override both, it should implement a MONGO_INITIALIZER_GENERAL
+ * with CyrusSaslAllocatorsAndMutexes and CyrusSaslClientContext as dependents, or
+ * initialize the library before calling mongo::runGlobalInitializersOrDie().
+ */
+ MONGO_INITIALIZER_WITH_PREREQUISITES(CyrusSaslClientContext,
+ ("NativeSaslClientContext",
+ "CyrusSaslAllocatorsAndMutexes"))
+ (InitializerContext* context) {
+
+ static sasl_callback_t saslClientGlobalCallbacks[] =
+ { { SASL_CB_LOG, SaslCallbackFn(saslClientLogSwallow), NULL /* context */ },
+ { SASL_CB_LIST_END } };
+
+ // If the client application has previously called sasl_client_init(), the callbacks passed
+ // in here are ignored.
+ //
+ // TODO: Call sasl_client_done() at shutdown when we have a story for orderly shutdown.
+ int result = sasl_client_init(saslClientGlobalCallbacks);
+ if (result != SASL_OK) {
+ return Status(ErrorCodes::UnknownError,
+ mongoutils::str::stream() <<
+ "Could not initialize sasl client components (" <<
+ sasl_errstring(result, NULL, NULL) <<
+ ")");
+ }
+
+ SaslClientSession::create = createCyrusSaslClientSession;
+ return Status::OK();
+ }
+
+ /**
+ * Callback registered on the sasl_conn_t underlying a CyrusSaslClientSession to allow the Cyrus SASL
+ * library to query for the authentication id and other simple string configuration parameters.
+ *
+ * Note that in Mongo, the authentication and authorization ids (authid and authzid) are always
+ * the same. These correspond to SASL_CB_AUTHNAME and SASL_CB_USER.
+ */
+ int saslClientGetSimple(void* context,
+ int id,
+ const char** result,
+ unsigned* resultLen) throw () {
+ CyrusSaslClientSession* session = static_cast<CyrusSaslClientSession*>(context);
+ if (!session || !result)
+ return SASL_BADPARAM;
+
+ CyrusSaslClientSession::Parameter requiredParameterId;
+ switch (id) {
+ case SASL_CB_AUTHNAME:
+ case SASL_CB_USER:
+ requiredParameterId = CyrusSaslClientSession::parameterUser;
+ break;
+ default:
+ return SASL_FAIL;
+ }
+
+ if (!session->hasParameter(requiredParameterId))
+ return SASL_FAIL;
+ StringData value = session->getParameter(requiredParameterId);
+ *result = value.rawData();
+ if (resultLen)
+ *resultLen = static_cast<unsigned>(value.size());
+ return SASL_OK;
+ }
+
+ /**
+ * Callback registered on the sasl_conn_t underlying a CyrusSaslClientSession to allow
+ * the Cyrus SASL library to query for the password data.
+ */
+ int saslClientGetPassword(sasl_conn_t* conn,
+ void* context,
+ int id,
+ sasl_secret_t** outSecret) throw () {
+
+ CyrusSaslClientSession* session = static_cast<CyrusSaslClientSession*>(context);
+ if (!session || !outSecret)
+ return SASL_BADPARAM;
+
+ sasl_secret_t* secret = session->getPasswordAsSecret();
+ if (secret == NULL) {
+ sasl_seterror(conn, 0, "No password data provided");
+ return SASL_FAIL;
+ }
+
+ *outSecret = secret;
+ return SASL_OK;
+ }
+} // namespace
+
+ CyrusSaslClientSession::CyrusSaslClientSession() :
+ SaslClientSession(),
+ _saslConnection(NULL),
+ _step(0),
+ _done(false) {
+
+ const sasl_callback_t callbackTemplate[maxCallbacks] = {
+ { SASL_CB_AUTHNAME, SaslCallbackFn(saslClientGetSimple), this },
+ { SASL_CB_USER, SaslCallbackFn(saslClientGetSimple), this },
+ { SASL_CB_PASS, SaslCallbackFn(saslClientGetPassword), this },
+ { SASL_CB_LIST_END }
+ };
+ std::copy(callbackTemplate, callbackTemplate + maxCallbacks, _callbacks);
+ }
+
+ CyrusSaslClientSession::~CyrusSaslClientSession() {
+ sasl_dispose(&_saslConnection);
+ }
+
+ void CyrusSaslClientSession::setParameter(Parameter id, const StringData& value) {
+ fassert(18665, id >= 0 && id < numParameters);
+ if (id == parameterPassword) {
+ // The parameterPassword is stored as a sasl_secret_t, while other
+ // parameters are stored directly. This facilitates memory ownership management for
+ // getPasswordAsSecret().
+ _secret.reset(new char[sizeof(sasl_secret_t) + value.size() + 1]);
+ sasl_secret_t* secret =
+ static_cast<sasl_secret_t*>(static_cast<void*>(_secret.get()));
+ secret->len = value.size();
+ value.copyTo(static_cast<char*>(static_cast<void*>(&secret->data[0])), false);
+ }
+ SaslClientSession::setParameter(id, value);
+ }
+
+ sasl_secret_t* CyrusSaslClientSession::getPasswordAsSecret() {
+ // See comment in setParameter() about the special storage of parameterPassword.
+ return static_cast<sasl_secret_t*>(
+ static_cast<void*>(_secret.get()));
+ }
+
+ Status CyrusSaslClientSession::initialize() {
+ if (_saslConnection != NULL)
+ return Status(ErrorCodes::AlreadyInitialized,
+ "Cannot reinitialize CyrusSaslClientSession.");
+
+ int result = sasl_client_new(getParameter(parameterServiceName).toString().c_str(),
+ getParameter(parameterServiceHostname).toString().c_str(),
+ NULL,
+ NULL,
+ _callbacks,
+ 0,
+ &_saslConnection);
+
+ if (SASL_OK != result) {
+ return Status(ErrorCodes::UnknownError,
+ mongoutils::str::stream() << sasl_errstring(result, NULL, NULL));
+ }
+
+ return Status::OK();
+ }
+
+ Status CyrusSaslClientSession::step(const StringData& inputData, std::string* outputData) {
+ const char* output = NULL;
+ unsigned outputSize = 0xFFFFFFFF;
+
+ int result;
+ if (_step == 0) {
+ const char* actualMechanism;
+ result = sasl_client_start(_saslConnection,
+ getParameter(parameterMechanism).toString().c_str(),
+ NULL,
+ &output,
+ &outputSize,
+ &actualMechanism);
+ }
+ else {
+ result = sasl_client_step(_saslConnection,
+ inputData.rawData(),
+ static_cast<unsigned>(inputData.size()),
+ NULL,
+ &output,
+ &outputSize);
+ }
+ ++_step;
+ switch (result) {
+ case SASL_OK:
+ _done = true;
+ // Fall through
+ case SASL_CONTINUE:
+ *outputData = std::string(output, outputSize);
+ return Status::OK();
+ case SASL_NOMECH:
+ return Status(ErrorCodes::BadValue, sasl_errdetail(_saslConnection));
+ case SASL_BADAUTH:
+ return Status(ErrorCodes::AuthenticationFailed, sasl_errdetail(_saslConnection));
+ default:
+ return Status(ErrorCodes::ProtocolError, sasl_errdetail(_saslConnection));
+ }
+ }
+} // namespace
diff --git a/src/mongo/client/cyrus_sasl_client_session.h b/src/mongo/client/cyrus_sasl_client_session.h
new file mode 100644
index 00000000000..2b6d04fe95f
--- /dev/null
+++ b/src/mongo/client/cyrus_sasl_client_session.h
@@ -0,0 +1,85 @@
+/* Copyright 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/client/sasl_client_session.h"
+
+#include <sasl/sasl.h>
+
+namespace mongo {
+
+ /**
+ * Implementation of the client side of a SASL authentication conversation.
+ * using the Cyrus SASL library.
+ */
+ class MONGO_CLIENT_API CyrusSaslClientSession : public SaslClientSession {
+ MONGO_DISALLOW_COPYING(CyrusSaslClientSession);
+ public:
+
+ CyrusSaslClientSession();
+ ~CyrusSaslClientSession();
+
+ /**
+ * Overriding to store the password data in sasl_secret_t format
+ */
+ virtual void setParameter(Parameter id, const StringData& value);
+
+ /**
+ * Returns the value of the parameterPassword parameter in the form of a sasl_secret_t, used
+ * by the Cyrus SASL library's SASL_CB_PASS callback. The session object owns the storage
+ * referenced by the returned sasl_secret_t*, which will remain in scope according to the
+ * same rules as given for SaslClientSession::getParameter().
+ */
+ sasl_secret_t* getPasswordAsSecret();
+
+ virtual Status initialize();
+
+ virtual Status step(const StringData& inputData, std::string* outputData);
+
+ virtual bool isDone() const { return _done; }
+
+ private:
+ /// Maximum number of Cyrus SASL callbacks stored in _callbacks.
+ static const int maxCallbacks = 4;
+
+ /// Underlying Cyrus SASL library connection object.
+ sasl_conn_t* _saslConnection;
+
+ // Number of successfully completed conversation steps.
+ int _step;
+
+ /// See isDone().
+ bool _done;
+
+ /// Stored of password in sasl_secret_t format
+ boost::scoped_array<char> _secret;
+
+ /// Callbacks registered on _saslConnection for providing the Cyrus SASL library with
+ /// parameter values, etc.
+ sasl_callback_t _callbacks[maxCallbacks];
+ };
+
+} // namespace mongo
diff --git a/src/mongo/client/native_sasl_client_session.cpp b/src/mongo/client/native_sasl_client_session.cpp
new file mode 100644
index 00000000000..705626dc106
--- /dev/null
+++ b/src/mongo/client/native_sasl_client_session.cpp
@@ -0,0 +1,66 @@
+/* Copyright 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/client/native_sasl_client_session.h"
+
+#include "mongo/base/init.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace {
+
+ SaslClientSession* createNativeSaslClientSession() {
+ return new NativeSaslClientSession();
+ }
+
+ MONGO_INITIALIZER(NativeSaslClientContext)(InitializerContext* context) {
+ SaslClientSession::create = createNativeSaslClientSession;
+ return Status::OK();
+ }
+
+} // namespace
+
+ NativeSaslClientSession::NativeSaslClientSession() :
+ SaslClientSession(),
+ _step(0),
+ _done(false) {
+ }
+
+ NativeSaslClientSession::~NativeSaslClientSession() {}
+
+ Status NativeSaslClientSession::initialize() {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "SASL authentication not supported in client");
+ }
+
+ Status NativeSaslClientSession::step(const StringData& inputData, std::string* outputData) {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "SASL authentication not supported in client");
+ }
+} // namespace
diff --git a/src/mongo/client/native_sasl_client_session.h b/src/mongo/client/native_sasl_client_session.h
new file mode 100644
index 00000000000..d80b9cc57ed
--- /dev/null
+++ b/src/mongo/client/native_sasl_client_session.h
@@ -0,0 +1,57 @@
+/* Copyright 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/client/sasl_client_session.h"
+
+namespace mongo {
+
+ /**
+ * Implementation of the client side of a SASL authentication conversation using the
+ * native SASL implementation.
+ */
+ class MONGO_CLIENT_API NativeSaslClientSession : public SaslClientSession {
+ MONGO_DISALLOW_COPYING(NativeSaslClientSession);
+ public:
+
+ NativeSaslClientSession();
+ ~NativeSaslClientSession();
+
+ virtual Status initialize();
+
+ virtual Status step(const StringData& inputData, std::string* outputData);
+
+ virtual bool isDone() const { return _done; }
+
+ private:
+ /// Number of successfully completed conversation steps.
+ int _step;
+
+ /// See isDone().
+ bool _done;
+ };
+
+} // namespace mongo
diff --git a/src/mongo/client/sasl_client_authenticate_impl.cpp b/src/mongo/client/sasl_client_authenticate_impl.cpp
index 97d17467f4d..dd779a36884 100644
--- a/src/mongo/client/sasl_client_authenticate_impl.cpp
+++ b/src/mongo/client/sasl_client_authenticate_impl.cpp
@@ -196,21 +196,22 @@ namespace {
return ex.toStatus();
}
- SaslClientSession session;
- Status status = configureSession(&session, client, targetDatabase, saslParameters);
+ boost::scoped_ptr<SaslClientSession> session(SaslClientSession::create());
+ Status status = configureSession(session.get(), client, targetDatabase, saslParameters);
+
if (!status.isOK())
return status;
BSONObj saslFirstCommandPrefix = BSON(
saslStartCommandName << 1 <<
saslCommandMechanismFieldName <<
- session.getParameter(SaslClientSession::parameterMechanism));
+ session->getParameter(SaslClientSession::parameterMechanism));
BSONObj saslFollowupCommandPrefix = BSON(saslContinueCommandName << 1);
BSONObj saslCommandPrefix = saslFirstCommandPrefix;
BSONObj inputObj = BSON(saslCommandPayloadFieldName << "");
bool isServerDone = false;
- while (!session.isDone()) {
+ while (!session->isDone()) {
std::string payload;
BSONType type;
@@ -221,7 +222,7 @@ namespace {
LOG(saslLogLevel) << "sasl client input: " << base64::encode(payload) << endl;
std::string responsePayload;
- status = session.step(payload, &responsePayload);
+ status = session->step(payload, &responsePayload);
if (!status.isOK())
return status;
diff --git a/src/mongo/client/sasl_client_session.cpp b/src/mongo/client/sasl_client_session.cpp
index 872c2043e18..06becc9b095 100644
--- a/src/mongo/client/sasl_client_session.cpp
+++ b/src/mongo/client/sasl_client_session.cpp
@@ -35,214 +35,22 @@
#include "mongo/util/signal_handlers_synchronous.h"
namespace mongo {
-namespace {
+ SaslClientSession::SaslClientSessionFactoryFn SaslClientSession::create = NULL;
+
+ SaslClientSession::SaslClientSession() {}
- /*
- * Allocator functions to be used by the SASL library, if the client
- * doesn't initialize the library for us.
- */
-
-// Version 2.1.26 is the first version to use size_t in the allocator signatures
-#if (SASL_VERSION_FULL >= ((2 << 16) | (1 << 8) | 26))
- typedef size_t SaslAllocSize;
-#else
- typedef unsigned long SaslAllocSize;
-#endif
-
- typedef int(*SaslCallbackFn)();
-
- void* saslOurMalloc(SaslAllocSize sz) {
- return mongoMalloc(sz);
- }
-
- void* saslOurCalloc(SaslAllocSize count, SaslAllocSize size) {
- void* ptr = calloc(count, size);
- if (!ptr) {
- reportOutOfMemoryErrorAndExit();
- }
- return ptr;
- }
-
- void* saslOurRealloc(void* ptr, SaslAllocSize sz) {
- return mongoRealloc(ptr, sz);
- }
-
- /*
- * Mutex functions to be used by the SASL library, if the client doesn't initialize the library
- * for us.
- */
-
- void* saslMutexAlloc(void) {
- return new SimpleMutex("sasl");
- }
-
- int saslMutexLock(void* mutex) {
- static_cast<SimpleMutex*>(mutex)->lock();
- return SASL_OK;
- }
-
- int saslMutexUnlock(void* mutex) {
- static_cast<SimpleMutex*>(mutex)->unlock();
- return SASL_OK;
- }
-
- void saslMutexFree(void* mutex) {
- delete static_cast<SimpleMutex*>(mutex);
- }
-
- /**
- * Configures the SASL library to use allocator and mutex functions we specify,
- * unless the client application has previously initialized the SASL library.
- */
- MONGO_INITIALIZER(CyrusSaslAllocatorsAndMutexes)(InitializerContext*) {
- sasl_set_alloc(saslOurMalloc,
- saslOurCalloc,
- saslOurRealloc,
- free);
-
- sasl_set_mutex(saslMutexAlloc,
- saslMutexLock,
- saslMutexUnlock,
- saslMutexFree);
- return Status::OK();
- }
-
- int saslClientLogSwallow(void *context, int priority, const char *message) {
- return SASL_OK; // do nothing
- }
-
- /**
- * Initializes the client half of the SASL library, but is effectively a no-op if the client
- * application has already done it.
- *
- * If a client wishes to override this initialization but keep the allocator and mutex
- * initialization, it should implement a MONGO_INITIALIZER_GENERAL with
- * CyrusSaslAllocatorsAndMutexes as a prerequisite and SaslClientContext as a dependent. If it
- * wishes to override both, it should implement a MONGO_INITIALIZER_GENERAL with
- * CyrusSaslAllocatorsAndMutexes and SaslClientContext as dependents, or initialize the library
- * before calling mongo::runGlobalInitializersOrDie().
- */
- MONGO_INITIALIZER_WITH_PREREQUISITES(SaslClientContext, ("CyrusSaslAllocatorsAndMutexes"))(
- InitializerContext* context) {
-
- static sasl_callback_t saslClientGlobalCallbacks[] =
- { { SASL_CB_LOG, SaslCallbackFn(saslClientLogSwallow), NULL /* context */ },
- { SASL_CB_LIST_END } };
-
- // If the client application has previously called sasl_client_init(), the callbacks passed
- // in here are ignored.
- //
- // TODO: Call sasl_client_done() at shutdown when we have a story for orderly shutdown.
- int result = sasl_client_init(saslClientGlobalCallbacks);
- if (result != SASL_OK) {
- return Status(ErrorCodes::UnknownError,
- mongoutils::str::stream() <<
- "Could not initialize sasl client components (" <<
- sasl_errstring(result, NULL, NULL) <<
- ")");
- }
- return Status::OK();
- }
-
- /**
- * Callback registered on the sasl_conn_t underlying a SaslClientSession to allow the Cyrus SASL
- * library to query for the authentication id and other simple string configuration parameters.
- *
- * Note that in Mongo, the authentication and authorization ids (authid and authzid) are always
- * the same. These correspond to SASL_CB_AUTHNAME and SASL_CB_USER.
- */
- int saslClientGetSimple(void* context,
- int id,
- const char** result,
- unsigned* resultLen) throw () {
- SaslClientSession* session = static_cast<SaslClientSession*>(context);
- if (!session || !result)
- return SASL_BADPARAM;
-
- SaslClientSession::Parameter requiredParameterId;
- switch (id) {
- case SASL_CB_AUTHNAME:
- case SASL_CB_USER:
- requiredParameterId = SaslClientSession::parameterUser;
- break;
- default:
- return SASL_FAIL;
- }
-
- if (!session->hasParameter(requiredParameterId))
- return SASL_FAIL;
- StringData value = session->getParameter(requiredParameterId);
- *result = value.rawData();
- if (resultLen)
- *resultLen = static_cast<unsigned>(value.size());
- return SASL_OK;
- }
-
- /**
- * Callback registered on the sasl_conn_t underlying a SaslClientSession to allow the Cyrus SASL
- * library to query for the password data.
- */
- int saslClientGetPassword(sasl_conn_t* conn,
- void* context,
- int id,
- sasl_secret_t** outSecret) throw () {
-
- SaslClientSession* session = static_cast<SaslClientSession*>(context);
- if (!session || !outSecret)
- return SASL_BADPARAM;
-
- sasl_secret_t* secret = session->getPasswordAsSecret();
- if (secret == NULL) {
- sasl_seterror(conn, 0, "No password data provided");
- return SASL_FAIL;
- }
-
- *outSecret = secret;
- return SASL_OK;
- }
-
-} // namespace
-
- SaslClientSession::SaslClientSession() :
- _saslConnection(NULL),
- _step(0),
- _done(false) {
-
- const sasl_callback_t callbackTemplate[maxCallbacks] = {
- { SASL_CB_AUTHNAME, SaslCallbackFn(saslClientGetSimple), this },
- { SASL_CB_USER, SaslCallbackFn(saslClientGetSimple), this },
- { SASL_CB_PASS, SaslCallbackFn(saslClientGetPassword), this },
- { SASL_CB_LIST_END }
- };
- std::copy(callbackTemplate, callbackTemplate + maxCallbacks, _callbacks);
- }
-
- SaslClientSession::~SaslClientSession() {
- sasl_dispose(&_saslConnection);
- }
+ SaslClientSession::~SaslClientSession() {}
void SaslClientSession::setParameter(Parameter id, const StringData& value) {
fassert(16807, id >= 0 && id < numParameters);
DataBuffer& buffer = _parameters[id];
- if (id == parameterPassword) {
- // The parameterPassword is stored as a sasl_secret_t inside its DataBuffer, while other
- // parameters are stored directly. This facilitates memory ownership management for
- // getPasswordAsSecret().
- buffer.size = sizeof(sasl_secret_t) + value.size();
- buffer.data.reset(new char[buffer.size + 1]);
- sasl_secret_t* secret =
- static_cast<sasl_secret_t*>(static_cast<void*>(buffer.data.get()));
- secret->len = value.size();
- value.copyTo(static_cast<char*>(static_cast<void*>(&secret->data[0])), false);
- }
- else {
- buffer.size = value.size();
- buffer.data.reset(new char[buffer.size + 1]);
- // Note that we append a terminal NUL to buffer.data, so it may be treated as a C-style
- // string. This is required for parameterServiceName, parameterServiceHostname,
- // parameterMechanism and parameterUser.
- value.copyTo(buffer.data.get(), true);
- }
+ buffer.size = value.size();
+ buffer.data.reset(new char[buffer.size + 1]);
+
+ // Note that we append a terminal NUL to buffer.data, so it may be treated as a C-style
+ // string. This is required for parameterServiceName, parameterServiceHostname,
+ // parameterMechanism and parameterUser.
+ value.copyTo(buffer.data.get(), true);
}
bool SaslClientSession::hasParameter(Parameter id) {
@@ -255,80 +63,8 @@ namespace {
if (!hasParameter(id))
return StringData();
- if (id == parameterPassword) {
- // See comment in setParameter() about the special storage of parameterPassword.
- sasl_secret_t* secret = getPasswordAsSecret();
- return StringData(static_cast<char*>(static_cast<void*>(secret->data)), secret->len);
- }
- else {
- DataBuffer& buffer = _parameters[id];
- return StringData(buffer.data.get(), buffer.size);
- }
- }
-
- sasl_secret_t* SaslClientSession::getPasswordAsSecret() {
- // See comment in setParameter() about the special storage of parameterPassword.
- return static_cast<sasl_secret_t*>(
- static_cast<void*>(_parameters[parameterPassword].data.get()));
- }
-
- Status SaslClientSession::initialize() {
- if (_saslConnection != NULL)
- return Status(ErrorCodes::AlreadyInitialized, "Cannot reinitialize SaslClientSession.");
-
- int result = sasl_client_new(_parameters[parameterServiceName].data.get(),
- _parameters[parameterServiceHostname].data.get(),
- NULL,
- NULL,
- _callbacks,
- 0,
- &_saslConnection);
-
- if (SASL_OK != result) {
- return Status(ErrorCodes::UnknownError,
- mongoutils::str::stream() << sasl_errstring(result, NULL, NULL));
- }
-
- return Status::OK();
- }
-
- Status SaslClientSession::step(const StringData& inputData, std::string* outputData) {
- const char* output = NULL;
- unsigned outputSize = 0xFFFFFFFF;
-
- int result;
- if (_step == 0) {
- const char* actualMechanism;
- result = sasl_client_start(_saslConnection,
- getParameter(parameterMechanism).toString().c_str(),
- NULL,
- &output,
- &outputSize,
- &actualMechanism);
- }
- else {
- result = sasl_client_step(_saslConnection,
- inputData.rawData(),
- static_cast<unsigned>(inputData.size()),
- NULL,
- &output,
- &outputSize);
- }
- ++_step;
- switch (result) {
- case SASL_OK:
- _done = true;
- // Fall through
- case SASL_CONTINUE:
- *outputData = std::string(output, outputSize);
- return Status::OK();
- case SASL_NOMECH:
- return Status(ErrorCodes::BadValue, sasl_errdetail(_saslConnection));
- case SASL_BADAUTH:
- return Status(ErrorCodes::AuthenticationFailed, sasl_errdetail(_saslConnection));
- default:
- return Status(ErrorCodes::ProtocolError, sasl_errdetail(_saslConnection));
- }
+ DataBuffer& buffer = _parameters[id];
+ return StringData(buffer.data.get(), buffer.size);
}
} // namespace mongo
diff --git a/src/mongo/client/sasl_client_session.h b/src/mongo/client/sasl_client_session.h
index 552c0653fef..8f06bcbe6ab 100644
--- a/src/mongo/client/sasl_client_session.h
+++ b/src/mongo/client/sasl_client_session.h
@@ -26,19 +26,18 @@
*/
#include <boost/scoped_array.hpp>
-#include <sasl/sasl.h>
#include <string>
-#include <vector>
#include "mongo/base/disallow_copying.h"
#include "mongo/base/status.h"
#include "mongo/base/string_data.h"
#include "mongo/client/export_macros.h"
+#include "mongo/stdx/functional.h"
namespace mongo {
/**
- * Implementation of the client side of a SASL authentication conversation.
+ * Base class for the client side of a SASL authentication conversation.
*
* To use, create an instance, then use setParameter() to configure the authentication
* parameters. Once all parameters are set, call initialize() to initialize the client state
@@ -53,6 +52,9 @@ namespace mongo {
class MONGO_CLIENT_API SaslClientSession {
MONGO_DISALLOW_COPYING(SaslClientSession);
public:
+ typedef stdx::function<SaslClientSession* ()> SaslClientSessionFactoryFn;
+ static SaslClientSessionFactoryFn create;
+
/**
* Identifiers of parameters used to configure a SaslClientSession.
*/
@@ -66,7 +68,7 @@ namespace mongo {
};
SaslClientSession();
- ~SaslClientSession();
+ virtual ~SaslClientSession();
/**
* Sets the parameter identified by "id" to "value".
@@ -77,12 +79,12 @@ namespace mongo {
*
* The session object makes and owns a copy of the data in "value".
*/
- void setParameter(Parameter id, const StringData& value);
+ virtual void setParameter(Parameter id, const StringData& value);
/**
* Returns true if "id" identifies a parameter previously set by a call to setParameter().
*/
- bool hasParameter(Parameter id);
+ virtual bool hasParameter(Parameter id);
/**
* Returns the value of a previously set parameter.
@@ -94,22 +96,14 @@ namespace mongo {
* valid until setParameter() is called with the same value of "id", or the session object
* goes out of scope.
*/
- StringData getParameter(Parameter id);
-
- /**
- * Returns the value of the parameterPassword parameter in the form of a sasl_secret_t, used
- * by the Cyrus SASL library's SASL_CB_PASS callback. The session object owns the storage
- * referenced by the returned sasl_secret_t*, which will remain in scope according to the
- * same rules as given for getParameter(), above.
- */
- sasl_secret_t* getPasswordAsSecret();
+ virtual StringData getParameter(Parameter id);
/**
* Initializes a session for use.
*
* Call exactly once, after setting any parameters you intend to set via setParameter().
*/
- Status initialize();
+ virtual Status initialize() = 0;
/**
* Takes one step of the SASL protocol on behalf of the client.
@@ -126,12 +120,12 @@ namespace mongo {
* determine if the conversation has completed. When step() returns Status::OK() and
* isDone() returns true, authentication has completed successfully.
*/
- Status step(const StringData& inputData, std::string* outputData);
+ virtual Status step(const StringData& inputData, std::string* outputData) = 0;
/**
* Returns true if the authentication completed successfully.
*/
- bool isDone() const { return _done; }
+ virtual bool isDone() const = 0;
private:
/**
@@ -142,24 +136,8 @@ namespace mongo {
size_t size;
};
- /// Maximum number of Cyrus SASL callbacks stored in _callbacks.
- static const int maxCallbacks = 4;
-
- /// Underlying Cyrus SASL library connection object.
- sasl_conn_t* _saslConnection;
-
- /// Callbacks registered on _saslConnection for providing the Cyrus SASL library with
- /// parameter values, etc.
- sasl_callback_t _callbacks[maxCallbacks];
-
/// Buffers for each of the settable parameters.
DataBuffer _parameters[numParameters];
-
- /// Number of successfully completed conversation steps.
- int _step;
-
- /// See isDone().
- bool _done;
};
} // namespace mongo
diff --git a/src/mongo/client/sasl_sspi.cpp b/src/mongo/client/sasl_sspi.cpp
index 1b08624bcb6..b6d72e01f46 100644
--- a/src/mongo/client/sasl_sspi.cpp
+++ b/src/mongo/client/sasl_sspi.cpp
@@ -492,7 +492,7 @@ namespace {
*/
MONGO_INITIALIZER_WITH_PREREQUISITES(SaslSspiClientPlugin,
("CyrusSaslAllocatorsAndMutexes",
- "SaslClientContext"))
+ "CyrusSaslClientContext"))
(InitializerContext*) {
int ret = sasl_client_add_plugin(sspiPluginName,
@@ -507,7 +507,7 @@ namespace {
}
MONGO_INITIALIZER_WITH_PREREQUISITES(SaslCramClientPlugin,
("CyrusSaslAllocatorsAndMutexes",
- "SaslClientContext"))
+ "CyrusSaslClientContext"))
(InitializerContext*) {
int ret = sasl_client_add_plugin("CRAMMD5",
crammd5_client_plug_init);
@@ -522,7 +522,7 @@ namespace {
MONGO_INITIALIZER_WITH_PREREQUISITES(SaslPlainClientPlugin,
("CyrusSaslAllocatorsAndMutexes",
- "SaslClientContext"))
+ "CyrusSaslClientContext"))
(InitializerContext*) {
int ret = sasl_client_add_plugin("PLAIN",
plain_client_plug_init);
diff --git a/src/mongo/db/auth/sasl_authentication_session.cpp b/src/mongo/db/auth/sasl_authentication_session.cpp
index 57d842999ec..4fba45521fb 100644
--- a/src/mongo/db/auth/sasl_authentication_session.cpp
+++ b/src/mongo/db/auth/sasl_authentication_session.cpp
@@ -47,7 +47,8 @@
#include "mongo/util/mongoutils/str.h"
namespace mongo {
- SaslAuthenticationSession::SaslSessionFactoryFn SaslAuthenticationSession::create = NULL;
+ SaslAuthenticationSession::SaslAuthenticationSessionFactoryFn
+ SaslAuthenticationSession::create = NULL;
// Mechanism name constants.
const char SaslAuthenticationSession::mechanismCRAMMD5[] = "CRAM-MD5";
diff --git a/src/mongo/db/auth/sasl_authentication_session.h b/src/mongo/db/auth/sasl_authentication_session.h
index e4b8ac42655..05c8aa8afab 100644
--- a/src/mongo/db/auth/sasl_authentication_session.h
+++ b/src/mongo/db/auth/sasl_authentication_session.h
@@ -51,8 +51,8 @@ namespace mongo {
MONGO_DISALLOW_COPYING(SaslAuthenticationSession);
public:
typedef stdx::function<SaslAuthenticationSession* (AuthorizationSession*)>
- SaslSessionFactoryFn;
- static SaslSessionFactoryFn create;
+ SaslAuthenticationSessionFactoryFn;
+ static SaslAuthenticationSessionFactoryFn create;
// Mechanism name constants.
static const char mechanismCRAMMD5[];