summaryrefslogtreecommitdiff
path: root/cpp
diff options
context:
space:
mode:
authorGordon Sim <gsim@apache.org>2008-04-22 10:33:12 +0000
committerGordon Sim <gsim@apache.org>2008-04-22 10:33:12 +0000
commit2b8e82875776feb8393c7791975acc9cf9fdb5e1 (patch)
tree1dda91e21db631a53fc796ffe6a12190895ba0db /cpp
parenta00e277846fb1ad432988e582506c7a4223e7d67 (diff)
downloadqpid-python-2b8e82875776feb8393c7791975acc9cf9fdb5e1.tar.gz
QPID-648: (based on patch from mfarrellee@redhat.com)
* apply authentication to final 0-10 codepath * consolidate conditional compilation of sasl-related code * improved handling of connection close during connection establishment in client git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@650439 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp')
-rw-r--r--cpp/src/Makefile.am2
-rw-r--r--cpp/src/qpid/broker/Broker.cpp1
-rw-r--r--cpp/src/qpid/broker/ConnectionHandler.cpp45
-rw-r--r--cpp/src/qpid/broker/ConnectionHandler.h3
-rw-r--r--cpp/src/qpid/broker/SaslAuthenticator.cpp242
-rw-r--r--cpp/src/qpid/broker/SaslAuthenticator.h47
-rw-r--r--cpp/src/qpid/client/ConnectionHandler.cpp13
-rw-r--r--cpp/src/qpid/client/ConnectionHandler.h4
8 files changed, 329 insertions, 28 deletions
diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am
index 1ddc86e5cd..608120a7e7 100644
--- a/cpp/src/Makefile.am
+++ b/cpp/src/Makefile.am
@@ -238,6 +238,7 @@ libqpidbroker_la_SOURCES = \
qpid/broker/RecoveryManagerImpl.cpp \
qpid/broker/RecoveredEnqueue.cpp \
qpid/broker/RecoveredDequeue.cpp \
+ qpid/broker/SaslAuthenticator.cpp \
qpid/broker/SemanticState.h \
qpid/broker/SemanticState.cpp \
qpid/broker/SessionState.h \
@@ -372,6 +373,7 @@ nobase_include_HEADERS = \
qpid/broker/RecoveryManager.h \
qpid/broker/RecoveryManagerImpl.h \
qpid/broker/SemanticHandler.h \
+ qpid/broker/SaslAuthenticator.h \
qpid/broker/SessionManager.h \
qpid/broker/System.h \
qpid/broker/Timer.h \
diff --git a/cpp/src/qpid/broker/Broker.cpp b/cpp/src/qpid/broker/Broker.cpp
index c32a8f889e..ec690a8acb 100644
--- a/cpp/src/qpid/broker/Broker.cpp
+++ b/cpp/src/qpid/broker/Broker.cpp
@@ -21,7 +21,6 @@
#include "config.h"
#include "Broker.h"
-#include "Connection.h"
#include "DirectExchange.h"
#include "FanOutExchange.h"
#include "HeadersExchange.h"
diff --git a/cpp/src/qpid/broker/ConnectionHandler.cpp b/cpp/src/qpid/broker/ConnectionHandler.cpp
index 79f9064b9d..c017520334 100644
--- a/cpp/src/qpid/broker/ConnectionHandler.cpp
+++ b/cpp/src/qpid/broker/ConnectionHandler.cpp
@@ -20,6 +20,8 @@
*
*/
+#include "config.h"
+
#include "ConnectionHandler.h"
#include "Connection.h"
#include "qpid/framing/ClientInvoker.h"
@@ -63,42 +65,41 @@ void ConnectionHandler::handle(framing::AMQFrame& frame)
}
}
-ConnectionHandler::ConnectionHandler(Connection& connection) : handler(new Handler(connection)) {
+ConnectionHandler::ConnectionHandler(Connection& connection) : handler(new Handler(connection)) {}
+
+ConnectionHandler::Handler::Handler(Connection& c) :
+ client(c.getOutput()), server(c.getOutput()),
+ connection(c), serverMode(false)
+{
FieldTable properties;
Array mechanisms(0x95);
- boost::shared_ptr<FieldValue> m(new Str16Value(PLAIN));
- mechanisms.add(m);
+
+ authenticator = SaslAuthenticator::createAuthenticator(c);
+ authenticator->getMechanisms(mechanisms);
+
Array locales(0x95);
boost::shared_ptr<FieldValue> l(new Str16Value(en_US));
locales.add(l);
- handler->serverMode = true;
- handler->client.start(properties, mechanisms, locales);
+ serverMode = true;
+ client.start(properties, mechanisms, locales);
}
+ConnectionHandler::Handler::~Handler() {}
-ConnectionHandler::Handler:: Handler(Connection& c) : client(c.getOutput()), server(c.getOutput()),
- connection(c), serverMode(false) {}
void ConnectionHandler::Handler::startOk(const framing::FieldTable& /*clientProperties*/,
- const string& mechanism,
- const string& response, const string& /*locale*/)
+ const string& mechanism,
+ const string& response,
+ const string& /*locale*/)
{
- //TODO: handle SASL mechanisms more cleverly
- if (mechanism == PLAIN) {
- if (response.size() > 0 && response[0] == (char) 0) {
- string temp = response.substr(1);
- string::size_type i = temp.find((char)0);
- string uid = temp.substr(0, i);
- string pwd = temp.substr(i + 1);
- //TODO: authentication
- connection.setUserId(uid);
- }
- }
- client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0);
+ authenticator->start(mechanism, response);
}
-void ConnectionHandler::Handler::secureOk(const string& /*response*/){}
+void ConnectionHandler::Handler::secureOk(const string& response)
+{
+ authenticator->step(response);
+}
void ConnectionHandler::Handler::tuneOk(uint16_t /*channelmax*/,
uint16_t framemax, uint16_t heartbeat)
diff --git a/cpp/src/qpid/broker/ConnectionHandler.h b/cpp/src/qpid/broker/ConnectionHandler.h
index 24fe8ce104..37246c7c1e 100644
--- a/cpp/src/qpid/broker/ConnectionHandler.h
+++ b/cpp/src/qpid/broker/ConnectionHandler.h
@@ -22,6 +22,7 @@
#define _ConnectionAdapter_
#include <memory>
+#include "SaslAuthenticator.h"
#include "qpid/framing/amqp_types.h"
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/AMQP_ClientOperations.h"
@@ -47,8 +48,10 @@ class ConnectionHandler : public framing::FrameHandler
framing::AMQP_ServerProxy::Connection010 server;
Connection& connection;
bool serverMode;
+ std::auto_ptr<SaslAuthenticator> authenticator;
Handler(Connection& connection);
+ ~Handler();
void startOk(const qpid::framing::FieldTable& clientProperties,
const std::string& mechanism, const std::string& response,
const std::string& locale);
diff --git a/cpp/src/qpid/broker/SaslAuthenticator.cpp b/cpp/src/qpid/broker/SaslAuthenticator.cpp
new file mode 100644
index 0000000000..9ca4069a12
--- /dev/null
+++ b/cpp/src/qpid/broker/SaslAuthenticator.cpp
@@ -0,0 +1,242 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 "config.h"
+
+#include "Connection.h"
+#include "qpid/log/Statement.h"
+
+#if HAVE_SASL
+#include <sasl/sasl.h>
+#endif
+
+using namespace qpid::framing;
+
+namespace qpid {
+namespace broker {
+
+
+class NullAuthenticator : public SaslAuthenticator
+{
+ Connection& connection;
+ framing::AMQP_ClientProxy::Connection010 client;
+public:
+ NullAuthenticator(Connection& connection);
+ ~NullAuthenticator();
+ void getMechanisms(framing::Array& mechanisms);
+ void start(const std::string& mechanism, const std::string& response);
+ void step(const std::string&) {}
+};
+
+#if HAVE_SASL
+
+class CyrusAuthenticator : public SaslAuthenticator
+{
+ sasl_conn_t *sasl_conn;
+ Connection& connection;
+ framing::AMQP_ClientProxy::Connection010 client;
+
+ void processAuthenticationStep(int code, const char *challenge, unsigned int challenge_len);
+
+public:
+ CyrusAuthenticator(Connection& connection);
+ ~CyrusAuthenticator();
+ void init();
+ void getMechanisms(framing::Array& mechanisms);
+ void start(const std::string& mechanism, const std::string& response);
+ void step(const std::string& response);
+};
+
+#else
+
+typedef NullAuthenticator CyrusAuthenticator;
+
+#endif
+
+std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c)
+{
+ if (c.getBroker().getOptions().auth) {
+ return std::auto_ptr<SaslAuthenticator>(new CyrusAuthenticator(c));
+ } else {
+ return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c));
+ }
+}
+
+NullAuthenticator::NullAuthenticator(Connection& c) : connection(c), client(c.getOutput()) {}
+NullAuthenticator::~NullAuthenticator() {}
+
+void NullAuthenticator::getMechanisms(Array& mechanisms)
+{
+ mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("ANONYMOUS")));
+}
+
+void NullAuthenticator::start(const string& /*mechanism*/, const string& /*response*/)
+{
+ QPID_LOG(warning, "SASL: No Authentication Performed");
+
+ // TODO: Figure out what should actually be set in this case
+ connection.setUserId("anonymous");
+
+ client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0);
+}
+
+
+#if HAVE_SASL
+
+CyrusAuthenticator::CyrusAuthenticator(Connection& c) : sasl_conn(0), connection(c), client(c.getOutput())
+{
+ init();
+}
+
+void CyrusAuthenticator::init()
+{
+ int code = sasl_server_new(BROKER_SASL_NAME,
+ NULL, NULL, NULL, NULL, NULL, 0,
+ &sasl_conn);
+
+ if (SASL_OK != code) {
+ QPID_LOG(info, "SASL: Connection creation failed: [" << code << "] " << sasl_errdetail(sasl_conn));
+
+ // TODO: Change this to an exception signaling
+ // server error, when one is available
+ throw CommandInvalidException("Unable to perform authentication");
+ }
+}
+
+CyrusAuthenticator::~CyrusAuthenticator()
+{
+ if (sasl_conn) {
+ sasl_dispose(&sasl_conn);
+ sasl_conn = 0;
+ }
+}
+
+void CyrusAuthenticator::getMechanisms(Array& mechanisms)
+{
+ const char *separator = " ";
+ const char *list;
+ unsigned int list_len;
+ int count;
+
+ int code = sasl_listmech(sasl_conn, NULL,
+ "", separator, "",
+ &list, &list_len,
+ &count);
+
+ if (SASL_OK != code) {
+ QPID_LOG(info, "SASL: Mechanism listing failed: " << sasl_errdetail(sasl_conn));
+
+ // TODO: Change this to an exception signaling
+ // server error, when one is available
+ throw CommandInvalidException("Mechanism listing failed");
+ } else {
+ string mechanism;
+ unsigned int start;
+ unsigned int end;
+
+ QPID_LOG(info, "SASL: Mechanism list: " << list);
+
+ end = 0;
+ do {
+ start = end;
+
+ // Seek to end of next mechanism
+ while (end < list_len && separator[0] != list[end])
+ end++;
+
+ // Record the mechanism
+ mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value(string(list, start, end - start))));
+ end++;
+ } while (end < list_len);
+ }
+}
+
+void CyrusAuthenticator::start(const string& mechanism, const string& response)
+{
+ const char *challenge;
+ unsigned int challenge_len;
+
+ QPID_LOG(info, "SASL: Starting authentication with mechanism: " << mechanism);
+ int code = sasl_server_start(sasl_conn,
+ mechanism.c_str(),
+ response.c_str(), response.length(),
+ &challenge, &challenge_len);
+
+ processAuthenticationStep(code, challenge, challenge_len);
+}
+
+void CyrusAuthenticator::step(const string& response)
+{
+ const char *challenge;
+ unsigned int challenge_len;
+
+ int code = sasl_server_step(sasl_conn,
+ response.c_str(), response.length(),
+ &challenge, &challenge_len);
+
+ processAuthenticationStep(code, challenge, challenge_len);
+}
+
+void CyrusAuthenticator::processAuthenticationStep(int code, const char *challenge, unsigned int challenge_len)
+{
+ if (SASL_OK == code) {
+ const void *uid;
+
+ code = sasl_getprop(sasl_conn, SASL_USERNAME, &uid);
+ if (SASL_OK != code) {
+ QPID_LOG(info, "SASL: Authentication succeeded, username unavailable");
+ // TODO: Change this to an exception signaling
+ // authentication failure, when one is available
+ throw ConnectionForcedException("Authenticated username unavailable");
+ }
+
+ QPID_LOG(info, "SASL: Authentication succeeded for: " << (char *)uid);
+
+ connection.setUserId((char *)uid);
+
+ client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0);
+ } else if (SASL_CONTINUE == code) {
+ string challenge_str(challenge, challenge_len);
+
+ QPID_LOG(debug, "SASL: sending challenge to client");
+
+ client.secure(challenge_str);
+ } else {
+ QPID_LOG(info, "SASL: Authentication failed: " << sasl_errdetail(sasl_conn));
+
+ // TODO: Change to more specific exceptions, when they are
+ // available
+ switch (code) {
+ case SASL_NOMECH:
+ throw ConnectionForcedException("Unsupported mechanism");
+ break;
+ case SASL_TRYAGAIN:
+ throw ConnectionForcedException("Transient failure, try again");
+ break;
+ default:
+ throw ConnectionForcedException("Authentication failed");
+ break;
+ }
+ }
+}
+#endif
+
+}}
diff --git a/cpp/src/qpid/broker/SaslAuthenticator.h b/cpp/src/qpid/broker/SaslAuthenticator.h
new file mode 100644
index 0000000000..c2d4ecf7c0
--- /dev/null
+++ b/cpp/src/qpid/broker/SaslAuthenticator.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ */
+#ifndef _SaslAuthenticator_
+#define _SaslAuthenticator_
+
+#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQP_ClientProxy.h"
+#include "qpid/Exception.h"
+#include <memory>
+
+namespace qpid {
+namespace broker {
+
+class Connection;
+
+class SaslAuthenticator
+{
+public:
+ virtual ~SaslAuthenticator() {}
+ virtual void getMechanisms(framing::Array& mechanisms) = 0;
+ virtual void start(const std::string& mechanism, const std::string& response) = 0;
+ virtual void step(const std::string& response) = 0;
+
+ static std::auto_ptr<SaslAuthenticator> createAuthenticator(Connection& connection);
+};
+
+}}
+
+#endif
diff --git a/cpp/src/qpid/client/ConnectionHandler.cpp b/cpp/src/qpid/client/ConnectionHandler.cpp
index 13de271e3b..83cc357ded 100644
--- a/cpp/src/qpid/client/ConnectionHandler.cpp
+++ b/cpp/src/qpid/client/ConnectionHandler.cpp
@@ -42,7 +42,7 @@ const std::string INVALID_STATE_CLOSE_OK("close-ok received in invalid state");
}
ConnectionHandler::ConnectionHandler()
- : StateManager(NOT_STARTED), outHandler(*this), proxy(outHandler)
+ : StateManager(NOT_STARTED), outHandler(*this), proxy(outHandler), errorCode(200)
{
mechanism = PLAIN;
@@ -54,6 +54,7 @@ ConnectionHandler::ConnectionHandler()
version = framing::highestProtocolVersion;
ESTABLISHED.insert(FAILED);
+ ESTABLISHED.insert(CLOSED);
ESTABLISHED.insert(OPEN);
}
@@ -98,8 +99,8 @@ void ConnectionHandler::outgoing(AMQFrame& frame)
void ConnectionHandler::waitForOpen()
{
waitFor(ESTABLISHED);
- if (getState() == FAILED) {
- throw Exception("Failed to establish connection.");
+ if (getState() == FAILED || getState() == CLOSED) {
+ throw ConnectionException(errorCode, errorText);
}
}
@@ -108,7 +109,7 @@ void ConnectionHandler::close()
switch (getState()) {
case NEGOTIATING:
case OPENING:
- setState(FAILED);
+ fail("Connection closed before it was established");
break;
case OPEN:
setState(CLOSING);
@@ -128,6 +129,8 @@ void ConnectionHandler::checkState(STATES s, const std::string& msg)
void ConnectionHandler::fail(const std::string& message)
{
+ errorCode = 502;
+ errorText = message;
QPID_LOG(warning, message);
setState(FAILED);
}
@@ -172,6 +175,8 @@ void ConnectionHandler::close(uint16_t replyCode, const std::string& replyText)
{
proxy.closeOk();
setState(CLOSED);
+ errorCode = replyCode;
+ errorText = replyText;
QPID_LOG(warning, "Broker closed connection: " << replyCode << ", " << replyText);
if (onError) {
onError(replyCode, replyText);
diff --git a/cpp/src/qpid/client/ConnectionHandler.h b/cpp/src/qpid/client/ConnectionHandler.h
index b298b02701..2ce36d6991 100644
--- a/cpp/src/qpid/client/ConnectionHandler.h
+++ b/cpp/src/qpid/client/ConnectionHandler.h
@@ -70,7 +70,9 @@ class ConnectionHandler : private StateManager,
};
Adapter outHandler;
- framing::AMQP_ServerProxy::Connection010 proxy;
+ framing::AMQP_ServerProxy::Connection010 proxy;
+ uint16_t errorCode;
+ std::string errorText;
void checkState(STATES s, const std::string& msg);