From bcb149706cdace4a333a811969e473451d9ab331 Mon Sep 17 00:00:00 2001 From: Michael Goulish Date: Wed, 20 Oct 2010 08:03:36 +0000 Subject: SASLizing Interbroker Links ------------------------------------------------------------- 1. Brokers already knew how to handle the server side of SASLized links, but not the client side. So we promoted the client-side SASL code from the client library to the common library so that the broker could also use it. This affected SaslFactory.{h,cpp} and Sasl.h TODO -- can the server-side and client-side code be unified here? 2. Some of the SASL verbs in broker/ConnectionHandler.cpp are expanded: start, secure, tune. 3. broker/SecureConnection is altered to get the client-broker and the server-broker to agree on when the security layer should be inserted. 4. the python tool qpid-route is modified so that, in the "route add" command, you can specify the security mechanism for SASL to use. TODO -- should we also pass in {min,max}SSF ? 5. Changes in broker/LinkRegistry to allow the information input by qpid-route to be passed up to where it is needed. 6. A bash script test run by "make check" that creates a SASLized federation link and sends some messages down it. TODO - write a python unit test instead of a bash script. I think I uncovered a bug in the python code when I tried. 7. NOTE - testing for this feature does not work with versions of SASL earlier than 2.1.22, becuase I can't tell SASL to use a SASL database file in a nonstandard location. The test is disabled for earlier versions. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk/qpid@1024541 13f79535-47bb-0310-9956-ffa450edef68 --- cpp/src/CMakeLists.txt | 2 +- cpp/src/Makefile.am | 6 +- cpp/src/qpid/Sasl.h | 60 +++++ cpp/src/qpid/SaslFactory.cpp | 415 ++++++++++++++++++++++++++++++ cpp/src/qpid/SaslFactory.h | 47 ++++ cpp/src/qpid/broker/Connection.cpp | 32 +++ cpp/src/qpid/broker/Connection.h | 4 + cpp/src/qpid/broker/ConnectionHandler.cpp | 104 +++++++- cpp/src/qpid/broker/ConnectionHandler.h | 15 ++ cpp/src/qpid/broker/LinkRegistry.cpp | 36 +++ cpp/src/qpid/broker/LinkRegistry.h | 4 + cpp/src/qpid/broker/SecureConnection.cpp | 5 +- cpp/src/qpid/broker/SecureConnection.h | 2 +- cpp/src/qpid/client/ConnectionHandler.cpp | 10 +- cpp/src/qpid/client/ConnectionHandler.h | 2 +- cpp/src/qpid/client/Sasl.h | 64 ----- cpp/src/qpid/client/SaslFactory.cpp | 385 --------------------------- cpp/src/qpid/client/SaslFactory.h | 48 ---- cpp/src/tests/sasl.mk | 1 + cpp/src/tests/sasl_fed | 152 +++++++++++ 20 files changed, 880 insertions(+), 514 deletions(-) create mode 100644 cpp/src/qpid/Sasl.h create mode 100644 cpp/src/qpid/SaslFactory.cpp create mode 100644 cpp/src/qpid/SaslFactory.h delete mode 100644 cpp/src/qpid/client/Sasl.h delete mode 100644 cpp/src/qpid/client/SaslFactory.cpp delete mode 100644 cpp/src/qpid/client/SaslFactory.h create mode 100755 cpp/src/tests/sasl_fed (limited to 'cpp/src') diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index e52ccb6777..ba2a866cfb 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -712,7 +712,6 @@ else (CMAKE_SYSTEM_NAME STREQUAL Windows) ) set (qpidclient_platform_SOURCES - qpid/client/SaslFactory.cpp ) set (qpidd_platform_SOURCES @@ -732,6 +731,7 @@ set (qpidcommon_SOURCES qpid/Options.cpp qpid/Plugin.cpp qpid/RefCountedBuffer.cpp + qpid/SaslFactory.cpp qpid/SessionState.cpp qpid/SessionId.cpp qpid/StringUtils.cpp diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am index 0ce1480825..9a6f7f3949 100644 --- a/cpp/src/Makefile.am +++ b/cpp/src/Makefile.am @@ -331,6 +331,9 @@ libqpidcommon_la_SOURCES += \ qpid/RefCounted.h \ qpid/RefCountedBuffer.cpp \ qpid/RefCountedBuffer.h \ + qpid/Sasl.h \ + qpid/SaslFactory.cpp \ + qpid/SaslFactory.h \ qpid/Serializer.h \ qpid/SessionId.cpp \ qpid/SessionState.cpp \ @@ -692,9 +695,6 @@ libqpidclient_la_SOURCES = \ qpid/client/QueueOptions.cpp \ qpid/client/Results.cpp \ qpid/client/Results.h \ - qpid/client/Sasl.h \ - qpid/client/SaslFactory.cpp \ - qpid/client/SaslFactory.h \ qpid/client/SessionBase_0_10.cpp \ qpid/client/SessionBase_0_10Access.h \ qpid/client/SessionImpl.cpp \ diff --git a/cpp/src/qpid/Sasl.h b/cpp/src/qpid/Sasl.h new file mode 100644 index 0000000000..9a9d61b037 --- /dev/null +++ b/cpp/src/qpid/Sasl.h @@ -0,0 +1,60 @@ +#ifndef QPID_SASL_H +#define QPID_SASL_H + +/* + * + * 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 +#include +#include "qpid/sys/IntegerTypes.h" + +namespace qpid { + +namespace sys { +class SecurityLayer; +struct SecuritySettings; +} + +/** + * Interface to SASL support. This class is implemented by platform-specific + * SASL providers. + */ +class Sasl +{ + public: + /** + * Start SASL negotiation with the broker. + * + * @param mechanisms Comma-separated list of the SASL mechanism the + * client supports. + * @param externalSecuritySettings security related details from the underlying transport + */ + virtual std::string start(const std::string& mechanisms, + const qpid::sys::SecuritySettings* externalSecuritySettings = 0) = 0; + virtual std::string step(const std::string& challenge) = 0; + virtual std::string getMechanism() = 0; + virtual std::string getUserId() = 0; + virtual std::auto_ptr getSecurityLayer(uint16_t maxFrameSize) = 0; + virtual ~Sasl() {} +}; +} // namespace qpid + +#endif /*!QPID_SASL_H*/ diff --git a/cpp/src/qpid/SaslFactory.cpp b/cpp/src/qpid/SaslFactory.cpp new file mode 100644 index 0000000000..28c27f7529 --- /dev/null +++ b/cpp/src/qpid/SaslFactory.cpp @@ -0,0 +1,415 @@ +/* + * + * 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 "qpid//SaslFactory.h" +#include +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef HAVE_SASL + +namespace qpid { + +//Null implementation + +SaslFactory::SaslFactory() {} + +SaslFactory::~SaslFactory() {} + +SaslFactory& SaslFactory::getInstance() +{ + qpid::sys::Mutex::ScopedLock l(lock); + if (!instance.get()) { + instance = std::auto_ptr(new SaslFactory()); + } + return *instance; +} + +std::auto_ptr SaslFactory::create( const std::string &, const std::string &, const std::string &, const std::string &, int, int ) +{ + return std::auto_ptr(); +} + +qpid::sys::Mutex SaslFactory::lock; +std::auto_ptr SaslFactory::instance; + +} // namespace qpid + +#else + +#include "qpid/Exception.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/sys/SecurityLayer.h" +#include "qpid/sys/SecuritySettings.h" +#include "qpid/sys/cyrus/CyrusSecurityLayer.h" +#include "qpid/log/Statement.h" +#include +#include + +namespace qpid { + +using qpid::sys::SecurityLayer; +using qpid::sys::SecuritySettings; +using qpid::sys::cyrus::CyrusSecurityLayer; +using qpid::framing::InternalErrorException; + +const size_t MAX_LOGIN_LENGTH = 50; + +struct CyrusSaslSettings +{ + CyrusSaslSettings ( ) : + username ( std::string(0) ), + password ( std::string(0) ), + service ( std::string(0) ), + host ( std::string(0) ), + minSsf ( 0 ), + maxSsf ( 0 ) + { + } + + CyrusSaslSettings ( const std::string & user, const std::string & password, const std::string & service, const std::string & host, int minSsf, int maxSsf ) : + username(user), + password(password), + service(service), + host(host), + minSsf(minSsf), + maxSsf(maxSsf) + { + } + + std::string username, + password, + service, + host; + + int minSsf, + maxSsf; +}; + + +class CyrusSasl : public Sasl +{ + public: + CyrusSasl(const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf); + ~CyrusSasl(); + std::string start(const std::string& mechanisms, const SecuritySettings* externalSettings); + std::string step(const std::string& challenge); + std::string getMechanism(); + std::string getUserId(); + std::auto_ptr getSecurityLayer(uint16_t maxFrameSize); + private: + sasl_conn_t* conn; + sasl_callback_t callbacks[5];//realm, user, authname, password, end-of-list + CyrusSaslSettings settings; + std::string input; + std::string mechanism; + char login[MAX_LOGIN_LENGTH]; + + void interact(sasl_interact_t* client_interact); +}; + +//sasl callback functions +int getUserFromSettings(void *context, int id, const char **result, unsigned *len); +int getPasswordFromSettings(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret); +typedef int CallbackProc(); + +qpid::sys::Mutex SaslFactory::lock; +std::auto_ptr SaslFactory::instance; + +SaslFactory::SaslFactory() +{ + sasl_callback_t* callbacks = 0; + int result = sasl_client_init(callbacks); + if (result != SASL_OK) { + throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errstring(result, 0, 0))); + } +} + +SaslFactory::~SaslFactory() +{ + sasl_done(); +} + +SaslFactory& SaslFactory::getInstance() +{ + qpid::sys::Mutex::ScopedLock l(lock); + if (!instance.get()) { + instance = std::auto_ptr(new SaslFactory()); + } + return *instance; +} + +std::auto_ptr SaslFactory::create(const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf) +{ + std::auto_ptr sasl(new CyrusSasl(username, password, serviceName, hostName, minSsf, maxSsf)); + return sasl; +} + +CyrusSasl::CyrusSasl(const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf) + : conn(0), settings(username, password, serviceName, hostName, minSsf, maxSsf) +{ + size_t i = 0; + + callbacks[i].id = SASL_CB_GETREALM; + callbacks[i].proc = 0; + callbacks[i++].context = 0; + + if (!settings.username.empty()) { + callbacks[i].id = SASL_CB_AUTHNAME; + callbacks[i].proc = (CallbackProc*) &getUserFromSettings; + callbacks[i++].context = &settings; + } + + callbacks[i].id = SASL_CB_PASS; + if (settings.password.empty()) { + callbacks[i].proc = 0; + callbacks[i++].context = 0; + } else { + callbacks[i].proc = (CallbackProc*) &getPasswordFromSettings; + callbacks[i++].context = &settings; + } + + callbacks[i].id = SASL_CB_LIST_END; + callbacks[i].proc = 0; + callbacks[i++].context = 0; +} + +CyrusSasl::~CyrusSasl() +{ + if (conn) { + sasl_dispose(&conn); + } +} + +namespace { + const std::string SSL("ssl"); +} + +std::string CyrusSasl::start(const std::string& mechanisms, const SecuritySettings* externalSettings) +{ + QPID_LOG(debug, "CyrusSasl::start(" << mechanisms << ")"); + int result = sasl_client_new(settings.service.c_str(), + settings.host.c_str(), + 0, 0, /* Local and remote IP address strings */ + callbacks, + 0, /* security flags */ + &conn); + + if (result != SASL_OK) throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errdetail(conn))); + + sasl_security_properties_t secprops; + + if (externalSettings) { + sasl_ssf_t external_ssf = (sasl_ssf_t) externalSettings->ssf; + if (external_ssf) { + int result = sasl_setprop(conn, SASL_SSF_EXTERNAL, &external_ssf); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external SSF: " << result)); + } + QPID_LOG(debug, "external SSF detected and set to " << external_ssf); + } + if (externalSettings->authid.size()) { + const char* external_authid = externalSettings->authid.c_str(); + result = sasl_setprop(conn, SASL_AUTH_EXTERNAL, external_authid); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external auth: " << result)); + } + QPID_LOG(debug, "external auth detected and set to " << external_authid); + } + } + + secprops.min_ssf = settings.minSsf; + secprops.max_ssf = settings.maxSsf; + secprops.maxbufsize = 65535; + + QPID_LOG(debug, "min_ssf: " << secprops.min_ssf << ", max_ssf: " << secprops.max_ssf); + + secprops.property_names = 0; + secprops.property_values = 0; + secprops.security_flags = 0;//TODO: provide means for application to configure these + + result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(conn))); + } + + sasl_interact_t* client_interact = 0; + const char *out = 0; + unsigned outlen = 0; + const char *chosenMechanism = 0; + + do { + result = sasl_client_start(conn, + mechanisms.c_str(), + &client_interact, + &out, + &outlen, + &chosenMechanism); + + if (result == SASL_INTERACT) { + interact(client_interact); + } + } while (result == SASL_INTERACT); + + if (result != SASL_CONTINUE && result != SASL_OK) { + throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errdetail(conn))); + } + + mechanism = std::string(chosenMechanism); + QPID_LOG(debug, "CyrusSasl::start(" << mechanisms << "): selected " + << mechanism << " response: '" << std::string(out, outlen) << "'"); + return std::string(out, outlen); +} + +std::string CyrusSasl::step(const std::string& challenge) +{ + sasl_interact_t* client_interact = 0; + const char *out = 0; + unsigned outlen = 0; + int result = 0; + do { + result = sasl_client_step(conn, /* our context */ + challenge.data(), /* the data from the server */ + challenge.size(), /* it's length */ + &client_interact, /* this should be + unallocated and NULL */ + &out, /* filled in on success */ + &outlen); /* filled in on success */ + + if (result == SASL_INTERACT) { + interact(client_interact); + } + } while (result == SASL_INTERACT); + + std::string response; + if (result == SASL_CONTINUE || result == SASL_OK) response = std::string(out, outlen); + else if (result != SASL_OK) { + throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errdetail(conn))); + } + QPID_LOG(debug, "CyrusSasl::step(" << challenge << "): " << response); + return response; +} + +std::string CyrusSasl::getMechanism() +{ + return mechanism; +} + +std::string CyrusSasl::getUserId() +{ + int propResult; + const void* operName; + + propResult = sasl_getprop(conn, SASL_USERNAME, &operName); + if (propResult == SASL_OK) + return std::string((const char*) operName); + + return std::string(); +} + +void CyrusSasl::interact(sasl_interact_t* client_interact) +{ + + if (client_interact->id == SASL_CB_PASS) { + char* password = getpass(client_interact->prompt); + input = std::string(password); + client_interact->result = input.data(); + client_interact->len = input.size(); + } else { + std::cout << client_interact->prompt; + if (client_interact->defresult) std::cout << " (" << client_interact->defresult << ")"; + std::cout << ": "; + if (std::cin >> input) { + client_interact->result = input.data(); + client_interact->len = input.size(); + } + } + +} + +std::auto_ptr CyrusSasl::getSecurityLayer(uint16_t maxFrameSize) +{ + const void* value(0); + int result = sasl_getprop(conn, SASL_SSF, &value); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(conn))); + } + uint ssf = *(reinterpret_cast(value)); + std::auto_ptr securityLayer; + if (ssf) { + QPID_LOG(info, "Installing security layer, SSF: "<< ssf); + securityLayer = std::auto_ptr(new CyrusSecurityLayer(conn, maxFrameSize)); + } + return securityLayer; +} + +int getUserFromSettings(void* context, int /*id*/, const char** result, unsigned* /*len*/) +{ + if (context) { + *result = ((CyrusSaslSettings*) context)->username.c_str(); + QPID_LOG(debug, "getUserFromSettings(): " << (*result)); + return SASL_OK; + } else { + return SASL_FAIL; + } +} + +namespace { +// Global map of secrets allocated for SASL connections via callback +// to getPasswordFromSettings. Ensures secrets are freed. +class SecretsMap { + typedef std::map Map; + Map map; + public: + void keep(sasl_conn_t* conn, void* secret) { + Map::iterator i = map.find(conn); + if (i != map.end()) free(i->second); + map[conn] = secret; + } + + ~SecretsMap() { + for (Map::iterator i = map.begin(); i != map.end(); ++i) + free(i->second); + } +}; +SecretsMap getPasswordFromSettingsSecrets; +} + +int getPasswordFromSettings(sasl_conn_t* conn, void* context, int /*id*/, sasl_secret_t** psecret) +{ + if (context) { + size_t length = ((CyrusSaslSettings*) context)->password.size(); + sasl_secret_t* secret = (sasl_secret_t*) malloc(sizeof(sasl_secret_t) + length); + getPasswordFromSettingsSecrets.keep(conn, secret); + secret->len = length; + memcpy(secret->data, ((CyrusSaslSettings*) context)->password.data(), length); + *psecret = secret; + return SASL_OK; + } else { + return SASL_FAIL; + } +} + +} // namespace qpid + +#endif diff --git a/cpp/src/qpid/SaslFactory.h b/cpp/src/qpid/SaslFactory.h new file mode 100644 index 0000000000..d9d83c494d --- /dev/null +++ b/cpp/src/qpid/SaslFactory.h @@ -0,0 +1,47 @@ +#ifndef QPID_SASLFACTORY_H +#define QPID_SASLFACTORY_H + +/* + * + * 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 "qpid/Sasl.h" +#include "qpid/sys/Mutex.h" +#include + +namespace qpid { + +/** + * Factory for instances of the Sasl interface through which Sasl + * support is provided to a ConnectionHandler. + */ +class SaslFactory +{ + public: + std::auto_ptr create(const std::string & userName, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf ); + static SaslFactory& getInstance(); + ~SaslFactory(); + private: + SaslFactory(); + static qpid::sys::Mutex lock; + static std::auto_ptr instance; +}; +} // namespace qpid + +#endif /*!QPID_SASLFACTORY_H*/ diff --git a/cpp/src/qpid/broker/Connection.cpp b/cpp/src/qpid/broker/Connection.cpp index d50f0c946a..98743cdae6 100644 --- a/cpp/src/qpid/broker/Connection.cpp +++ b/cpp/src/qpid/broker/Connection.cpp @@ -192,6 +192,38 @@ string Connection::getAuthMechanism() return links.getAuthMechanism(mgmtId); } +string Connection::getUsername ( ) +{ + if (!isLink) + return string("anonymous"); + + return links.getUsername(mgmtId); +} + +string Connection::getPassword ( ) +{ + if (!isLink) + return string(""); + + return links.getPassword(mgmtId); +} + +string Connection::getHost ( ) +{ + if (!isLink) + return string(""); + + return links.getHost(mgmtId); +} + +uint16_t Connection::getPort ( ) +{ + if (!isLink) + return 0; + + return links.getPort(mgmtId); +} + string Connection::getAuthCredentials() { if (!isLink) diff --git a/cpp/src/qpid/broker/Connection.h b/cpp/src/qpid/broker/Connection.h index c1b2b5a8fc..d978187e0c 100644 --- a/cpp/src/qpid/broker/Connection.h +++ b/cpp/src/qpid/broker/Connection.h @@ -115,6 +115,10 @@ class Connection : public sys::ConnectionInputHandler, void recordFromClient (framing::AMQFrame& frame); std::string getAuthMechanism(); std::string getAuthCredentials(); + std::string getUsername(); + std::string getPassword(); + std::string getHost(); + uint16_t getPort(); void notifyConnectionForced(const std::string& text); void setUserId(const std::string& uid); void raiseConnectEvent(); diff --git a/cpp/src/qpid/broker/ConnectionHandler.cpp b/cpp/src/qpid/broker/ConnectionHandler.cpp index c349bc7ac7..c812374d38 100644 --- a/cpp/src/qpid/broker/ConnectionHandler.cpp +++ b/cpp/src/qpid/broker/ConnectionHandler.cpp @@ -20,6 +20,7 @@ * */ +#include "qpid/SaslFactory.h" #include "qpid/broker/ConnectionHandler.h" #include "qpid/broker/Connection.h" #include "qpid/broker/SecureConnection.h" @@ -49,6 +50,7 @@ const std::string CLIENT_PROCESS_NAME("qpid.client_process"); const std::string CLIENT_PID("qpid.client_pid"); const std::string CLIENT_PPID("qpid.client_ppid"); const int SESSION_FLOW_CONTROL_VER = 1; +const std::string SPACE(" "); } void ConnectionHandler::close(connection::CloseCode code, const string& text) @@ -106,7 +108,10 @@ ConnectionHandler::Handler::Handler(Connection& c, bool isClient, bool isShadow) boost::shared_ptr l(new Str16Value(en_US)); locales.add(l); proxy.start(properties, mechanisms, locales); + } + + maxFrameSize = (64 * 1024) - 1; } @@ -230,33 +235,105 @@ void ConnectionHandler::Handler::heartbeat(){ } void ConnectionHandler::Handler::start(const FieldTable& serverProperties, - const framing::Array& /*mechanisms*/, + const framing::Array& supportedMechanisms, const framing::Array& /*locales*/) { - string mechanism = connection.getAuthMechanism(); + string requestedMechanism = connection.getAuthMechanism(); string response = connection.getAuthCredentials(); + std::string username = connection.getUsername(); + std::string password = connection.getPassword(); + std::string host = connection.getHost(); + std::string service("qpidd"); + + sasl = SaslFactory::getInstance().create( username, + password, + service, + host, + 0, // TODO -- mgoulish Fri Sep 24 06:41:26 EDT 2010 + 256 /* TODO -- mgoulish*/ ); + std::string supportedMechanismsList; + bool requestedMechanismIsSupported = false; + Array::const_iterator i; + + /* + If no specific mechanism has been requested, just make + a list of all of them, and assert that the one the caller + requested is there. ( If *any* are supported! ) + */ + if ( requestedMechanism.empty() ) { + for ( i = supportedMechanisms.begin(); i != supportedMechanisms.end(); ++i) { + if (i != supportedMechanisms.begin()) + supportedMechanismsList += SPACE; + supportedMechanismsList += (*i)->get(); + requestedMechanismIsSupported = true; + } + } + else { + requestedMechanismIsSupported = false; + /* + The caller has requested a mechanism. If it's available, + make sure it ends up at the head of the list. + */ + for ( i = supportedMechanisms.begin(); i != supportedMechanisms.end(); ++i) { + string currentMechanism = (*i)->get(); + + if ( requestedMechanism == currentMechanism ) { + requestedMechanismIsSupported = true; + supportedMechanismsList = currentMechanism + SPACE + supportedMechanismsList; + } else { + if (i != supportedMechanisms.begin()) + supportedMechanismsList += SPACE; + supportedMechanismsList += currentMechanism; + } + } + } + connection.setFederationPeerTag(serverProperties.getAsString(QPID_FED_TAG)); FieldTable ft; ft.setInt(QPID_FED_LINK,1); ft.setString(QPID_FED_TAG, connection.getBroker().getFederationTag()); - proxy.startOk(ft, mechanism, response, en_US); + + if (sasl.get()) { + string response = + sasl->start ( requestedMechanism.empty() + ? supportedMechanismsList + : requestedMechanism, + getSecuritySettings + ? getSecuritySettings() + : 0 + ); + proxy.startOk ( ft, sasl->getMechanism(), response, en_US ); + } + else { + string response = ((char)0) + username + ((char)0) + password; + proxy.startOk ( ft, requestedMechanism, response, en_US ); + } + } -void ConnectionHandler::Handler::secure(const string& /*challenge*/) +void ConnectionHandler::Handler::secure(const string& challenge ) { - proxy.secureOk(""); + if (sasl.get()) { + string response = sasl->step(challenge); + proxy.secureOk(response); + } + else { + proxy.secureOk(""); + } } void ConnectionHandler::Handler::tune(uint16_t channelMax, - uint16_t frameMax, + uint16_t maxFrameSizeProposed, uint16_t /*heartbeatMin*/, uint16_t heartbeatMax) { - connection.setFrameMax(frameMax); + maxFrameSize = std::min(maxFrameSize, maxFrameSizeProposed); + connection.setFrameMax(maxFrameSize); + connection.setHeartbeat(heartbeatMax); - proxy.tuneOk(channelMax, frameMax, heartbeatMax); + proxy.tuneOk(channelMax, maxFrameSize, heartbeatMax); proxy.open("/", Array(), true); } @@ -266,6 +343,17 @@ void ConnectionHandler::Handler::openOk(const framing::Array& knownHosts) Url url((*i)->get()); connection.getKnownHosts().push_back(url); } + + if (sasl.get()) { + std::auto_ptr securityLayer = sasl->getSecurityLayer(maxFrameSize); + + if ( securityLayer.get() ) { + secured->activateSecurityLayer(securityLayer, true); + } + + saslUserId = sasl->getUserId(); + } + isOpen = true; } diff --git a/cpp/src/qpid/broker/ConnectionHandler.h b/cpp/src/qpid/broker/ConnectionHandler.h index 6d55cab647..70882a24e9 100644 --- a/cpp/src/qpid/broker/ConnectionHandler.h +++ b/cpp/src/qpid/broker/ConnectionHandler.h @@ -22,6 +22,7 @@ #define _ConnectionAdapter_ #include +#include "qpid/Sasl.h" #include "qpid/broker/SaslAuthenticator.h" #include "qpid/framing/amqp_types.h" #include "qpid/framing/AMQFrame.h" @@ -33,8 +34,16 @@ #include "qpid/framing/ProtocolVersion.h" #include "qpid/Exception.h" #include "qpid/broker/AclModule.h" +#include "qpid/sys/SecurityLayer.h" + namespace qpid { + +namespace sys { +struct SecuritySettings; +} + + namespace broker { class Connection; @@ -79,6 +88,12 @@ class ConnectionHandler : public framing::FrameHandler void openOk(const framing::Array& knownHosts); void redirect(const std::string& host, const framing::Array& knownHosts); + + std::auto_ptr sasl; + typedef boost::function GetSecuritySettings; + GetSecuritySettings getSecuritySettings; /* query the transport for its security details */ + std::string saslUserId; + uint16_t maxFrameSize; }; std::auto_ptr handler; diff --git a/cpp/src/qpid/broker/LinkRegistry.cpp b/cpp/src/qpid/broker/LinkRegistry.cpp index 9d429a2dcc..ea14552cc1 100644 --- a/cpp/src/qpid/broker/LinkRegistry.cpp +++ b/cpp/src/qpid/broker/LinkRegistry.cpp @@ -312,6 +312,42 @@ std::string LinkRegistry::getAuthCredentials(const std::string& key) return result; } +std::string LinkRegistry::getUsername(const std::string& key) +{ + Link::shared_ptr link = findLink(key); + if (!link) + return string(); + + return link->getUsername(); +} + +std::string LinkRegistry::getHost(const std::string& key) +{ + Link::shared_ptr link = findLink(key); + if (!link) + return string(); + + return link->getHost(); +} + +uint16_t LinkRegistry::getPort(const std::string& key) +{ + Link::shared_ptr link = findLink(key); + if (!link) + return 0; + + return link->getPort(); +} + +std::string LinkRegistry::getPassword(const std::string& key) +{ + Link::shared_ptr link = findLink(key); + if (!link) + return string(); + + return link->getPassword(); +} + std::string LinkRegistry::getAuthIdentity(const std::string& key) { Link::shared_ptr link = findLink(key); diff --git a/cpp/src/qpid/broker/LinkRegistry.h b/cpp/src/qpid/broker/LinkRegistry.h index 52ab700cfc..a1931920d7 100644 --- a/cpp/src/qpid/broker/LinkRegistry.h +++ b/cpp/src/qpid/broker/LinkRegistry.h @@ -132,6 +132,10 @@ namespace broker { std::string getAuthMechanism (const std::string& key); std::string getAuthCredentials (const std::string& key); std::string getAuthIdentity (const std::string& key); + std::string getUsername (const std::string& key); + std::string getPassword (const std::string& key); + std::string getHost (const std::string& key); + uint16_t getPort (const std::string& key); /** * Called by links failing over to new address diff --git a/cpp/src/qpid/broker/SecureConnection.cpp b/cpp/src/qpid/broker/SecureConnection.cpp index 74aec239ca..5c1ebf3e8b 100644 --- a/cpp/src/qpid/broker/SecureConnection.cpp +++ b/cpp/src/qpid/broker/SecureConnection.cpp @@ -78,10 +78,13 @@ void SecureConnection:: setCodec(std::auto_ptr c) codec = c; } -void SecureConnection::activateSecurityLayer(std::auto_ptr sl) +void SecureConnection::activateSecurityLayer(std::auto_ptr sl, bool secureImmediately) { securityLayer = sl; securityLayer->init(codec.get()); + + if ( secureImmediately ) + secured = true; } }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SecureConnection.h b/cpp/src/qpid/broker/SecureConnection.h index 4a0cc50e34..1547faae1e 100644 --- a/cpp/src/qpid/broker/SecureConnection.h +++ b/cpp/src/qpid/broker/SecureConnection.h @@ -49,7 +49,7 @@ class SecureConnection : public qpid::sys::ConnectionCodec bool isClosed() const; framing::ProtocolVersion getVersion() const; void setCodec(std::auto_ptr); - void activateSecurityLayer(std::auto_ptr); + void activateSecurityLayer(std::auto_ptr, bool secureImmediately=false); private: std::auto_ptr codec; std::auto_ptr securityLayer; diff --git a/cpp/src/qpid/client/ConnectionHandler.cpp b/cpp/src/qpid/client/ConnectionHandler.cpp index e615878703..8dc1e8338a 100644 --- a/cpp/src/qpid/client/ConnectionHandler.cpp +++ b/cpp/src/qpid/client/ConnectionHandler.cpp @@ -21,7 +21,7 @@ #include "qpid/client/ConnectionHandler.h" -#include "qpid/client/SaslFactory.h" +#include "qpid/SaslFactory.h" #include "qpid/client/Bounds.h" #include "qpid/framing/amqp_framing.h" #include "qpid/framing/all_method_bodies.h" @@ -208,7 +208,13 @@ void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& me { checkState(NOT_STARTED, INVALID_STATE_START); setState(NEGOTIATING); - sasl = SaslFactory::getInstance().create(*this); + sasl = SaslFactory::getInstance().create( username, + password, + service, + host, + minSsf, + maxSsf + ); std::string mechlist; bool chosenMechanismSupported = mechanism.empty(); diff --git a/cpp/src/qpid/client/ConnectionHandler.h b/cpp/src/qpid/client/ConnectionHandler.h index 61709db174..6af2e987fb 100644 --- a/cpp/src/qpid/client/ConnectionHandler.h +++ b/cpp/src/qpid/client/ConnectionHandler.h @@ -23,7 +23,7 @@ #include "qpid/client/ChainableFrameHandler.h" #include "qpid/client/ConnectionSettings.h" -#include "qpid/client/Sasl.h" +#include "qpid/Sasl.h" #include "qpid/client/StateManager.h" #include "qpid/framing/AMQMethodBody.h" #include "qpid/framing/AMQP_HighestVersion.h" diff --git a/cpp/src/qpid/client/Sasl.h b/cpp/src/qpid/client/Sasl.h deleted file mode 100644 index 56735a5fc3..0000000000 --- a/cpp/src/qpid/client/Sasl.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef QPID_CLIENT_SASL_H -#define QPID_CLIENT_SASL_H - -/* - * - * 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 -#include -#include "qpid/sys/IntegerTypes.h" - -namespace qpid { - -namespace sys { -class SecurityLayer; -struct SecuritySettings; -} - -namespace client { - -struct ConnectionSettings; - -/** - * Interface to SASL support. This class is implemented by platform-specific - * SASL providers. - */ -class Sasl -{ - public: - /** - * Start SASL negotiation with the broker. - * - * @param mechanisms Comma-separated list of the SASL mechanism the - * client supports. - * @param externalSecuritySettings security related details from the underlying transport - */ - virtual std::string start(const std::string& mechanisms, - const qpid::sys::SecuritySettings* externalSecuritySettings = 0) = 0; - virtual std::string step(const std::string& challenge) = 0; - virtual std::string getMechanism() = 0; - virtual std::string getUserId() = 0; - virtual std::auto_ptr getSecurityLayer(uint16_t maxFrameSize) = 0; - virtual ~Sasl() {} -}; -}} // namespace qpid::client - -#endif /*!QPID_CLIENT_SASL_H*/ diff --git a/cpp/src/qpid/client/SaslFactory.cpp b/cpp/src/qpid/client/SaslFactory.cpp deleted file mode 100644 index 79acf3cd7c..0000000000 --- a/cpp/src/qpid/client/SaslFactory.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* - * - * 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 "qpid/client/SaslFactory.h" -#include "qpid/client/ConnectionSettings.h" -#include -#include - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifndef HAVE_SASL - -namespace qpid { -namespace client { - -//Null implementation - -SaslFactory::SaslFactory() {} - -SaslFactory::~SaslFactory() {} - -SaslFactory& SaslFactory::getInstance() -{ - qpid::sys::Mutex::ScopedLock l(lock); - if (!instance.get()) { - instance = std::auto_ptr(new SaslFactory()); - } - return *instance; -} - -std::auto_ptr SaslFactory::create(const ConnectionSettings&) -{ - return std::auto_ptr(); -} - -qpid::sys::Mutex SaslFactory::lock; -std::auto_ptr SaslFactory::instance; - -}} // namespace qpid::client - -#else - -#include "qpid/Exception.h" -#include "qpid/framing/reply_exceptions.h" -#include "qpid/sys/SecurityLayer.h" -#include "qpid/sys/SecuritySettings.h" -#include "qpid/sys/cyrus/CyrusSecurityLayer.h" -#include "qpid/log/Statement.h" -#include -#include - -namespace qpid { -namespace client { - -using qpid::sys::SecurityLayer; -using qpid::sys::SecuritySettings; -using qpid::sys::cyrus::CyrusSecurityLayer; -using qpid::framing::InternalErrorException; - -const size_t MAX_LOGIN_LENGTH = 50; - -class CyrusSasl : public Sasl -{ - public: - CyrusSasl(const ConnectionSettings&); - ~CyrusSasl(); - std::string start(const std::string& mechanisms, const SecuritySettings* externalSettings); - std::string step(const std::string& challenge); - std::string getMechanism(); - std::string getUserId(); - std::auto_ptr getSecurityLayer(uint16_t maxFrameSize); - private: - sasl_conn_t* conn; - sasl_callback_t callbacks[5];//realm, user, authname, password, end-of-list - ConnectionSettings settings; - std::string input; - std::string mechanism; - char login[MAX_LOGIN_LENGTH]; - - void interact(sasl_interact_t* client_interact); -}; - -//sasl callback functions -int getUserFromSettings(void *context, int id, const char **result, unsigned *len); -int getPasswordFromSettings(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret); -typedef int CallbackProc(); - -qpid::sys::Mutex SaslFactory::lock; -std::auto_ptr SaslFactory::instance; - -SaslFactory::SaslFactory() -{ - sasl_callback_t* callbacks = 0; - int result = sasl_client_init(callbacks); - if (result != SASL_OK) { - throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errstring(result, 0, 0))); - } -} - -SaslFactory::~SaslFactory() -{ - sasl_done(); -} - -SaslFactory& SaslFactory::getInstance() -{ - qpid::sys::Mutex::ScopedLock l(lock); - if (!instance.get()) { - instance = std::auto_ptr(new SaslFactory()); - } - return *instance; -} - -std::auto_ptr SaslFactory::create(const ConnectionSettings& settings) -{ - std::auto_ptr sasl(new CyrusSasl(settings)); - return sasl; -} - -CyrusSasl::CyrusSasl(const ConnectionSettings& s) : conn(0), settings(s) -{ - size_t i = 0; - - callbacks[i].id = SASL_CB_GETREALM; - callbacks[i].proc = 0; - callbacks[i++].context = 0; - - if (!settings.username.empty()) { - callbacks[i].id = SASL_CB_AUTHNAME; - callbacks[i].proc = (CallbackProc*) &getUserFromSettings; - callbacks[i++].context = &settings; - } - - callbacks[i].id = SASL_CB_PASS; - if (settings.password.empty()) { - callbacks[i].proc = 0; - callbacks[i++].context = 0; - } else { - callbacks[i].proc = (CallbackProc*) &getPasswordFromSettings; - callbacks[i++].context = &settings; - } - - callbacks[i].id = SASL_CB_LIST_END; - callbacks[i].proc = 0; - callbacks[i++].context = 0; -} - -CyrusSasl::~CyrusSasl() -{ - if (conn) { - sasl_dispose(&conn); - } -} - -namespace { - const std::string SSL("ssl"); -} - -std::string CyrusSasl::start(const std::string& mechanisms, const SecuritySettings* externalSettings) -{ - QPID_LOG(debug, "CyrusSasl::start(" << mechanisms << ")"); - int result = sasl_client_new(settings.service.c_str(), - settings.host.c_str(), - 0, 0, /* Local and remote IP address strings */ - callbacks, - 0, /* security flags */ - &conn); - - if (result != SASL_OK) throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errdetail(conn))); - - sasl_security_properties_t secprops; - - if (externalSettings) { - sasl_ssf_t external_ssf = (sasl_ssf_t) externalSettings->ssf; - if (external_ssf) { - int result = sasl_setprop(conn, SASL_SSF_EXTERNAL, &external_ssf); - if (result != SASL_OK) { - throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external SSF: " << result)); - } - QPID_LOG(debug, "external SSF detected and set to " << external_ssf); - } - if (externalSettings->authid.size()) { - const char* external_authid = externalSettings->authid.c_str(); - result = sasl_setprop(conn, SASL_AUTH_EXTERNAL, external_authid); - if (result != SASL_OK) { - throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external auth: " << result)); - } - QPID_LOG(debug, "external auth detected and set to " << external_authid); - } - } - - secprops.min_ssf = settings.minSsf; - secprops.max_ssf = settings.maxSsf; - secprops.maxbufsize = 65535; - - QPID_LOG(debug, "min_ssf: " << secprops.min_ssf << ", max_ssf: " << secprops.max_ssf); - - secprops.property_names = 0; - secprops.property_values = 0; - secprops.security_flags = 0;//TODO: provide means for application to configure these - - result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops); - if (result != SASL_OK) { - throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(conn))); - } - - sasl_interact_t* client_interact = 0; - const char *out = 0; - unsigned outlen = 0; - const char *chosenMechanism = 0; - - do { - result = sasl_client_start(conn, - mechanisms.c_str(), - &client_interact, - &out, - &outlen, - &chosenMechanism); - - if (result == SASL_INTERACT) { - interact(client_interact); - } - } while (result == SASL_INTERACT); - - if (result != SASL_CONTINUE && result != SASL_OK) { - throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errdetail(conn))); - } - - mechanism = std::string(chosenMechanism); - QPID_LOG(debug, "CyrusSasl::start(" << mechanisms << "): selected " - << mechanism << " response: '" << std::string(out, outlen) << "'"); - return std::string(out, outlen); -} - -std::string CyrusSasl::step(const std::string& challenge) -{ - sasl_interact_t* client_interact = 0; - const char *out = 0; - unsigned outlen = 0; - int result = 0; - do { - result = sasl_client_step(conn, /* our context */ - challenge.data(), /* the data from the server */ - challenge.size(), /* it's length */ - &client_interact, /* this should be - unallocated and NULL */ - &out, /* filled in on success */ - &outlen); /* filled in on success */ - - if (result == SASL_INTERACT) { - interact(client_interact); - } - } while (result == SASL_INTERACT); - - std::string response; - if (result == SASL_CONTINUE || result == SASL_OK) response = std::string(out, outlen); - else if (result != SASL_OK) { - throw InternalErrorException(QPID_MSG("Sasl error: " << sasl_errdetail(conn))); - } - QPID_LOG(debug, "CyrusSasl::step(" << challenge << "): " << response); - return response; -} - -std::string CyrusSasl::getMechanism() -{ - return mechanism; -} - -std::string CyrusSasl::getUserId() -{ - int propResult; - const void* operName; - - propResult = sasl_getprop(conn, SASL_USERNAME, &operName); - if (propResult == SASL_OK) - return std::string((const char*) operName); - - return std::string(); -} - -void CyrusSasl::interact(sasl_interact_t* client_interact) -{ - - if (client_interact->id == SASL_CB_PASS) { - char* password = getpass(client_interact->prompt); - input = std::string(password); - client_interact->result = input.data(); - client_interact->len = input.size(); - } else { - std::cout << client_interact->prompt; - if (client_interact->defresult) std::cout << " (" << client_interact->defresult << ")"; - std::cout << ": "; - if (std::cin >> input) { - client_interact->result = input.data(); - client_interact->len = input.size(); - } - } - -} - -std::auto_ptr CyrusSasl::getSecurityLayer(uint16_t maxFrameSize) -{ - const void* value(0); - int result = sasl_getprop(conn, SASL_SSF, &value); - if (result != SASL_OK) { - throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(conn))); - } - uint ssf = *(reinterpret_cast(value)); - std::auto_ptr securityLayer; - if (ssf) { - QPID_LOG(info, "Installing security layer, SSF: "<< ssf); - securityLayer = std::auto_ptr(new CyrusSecurityLayer(conn, maxFrameSize)); - } - return securityLayer; -} - -int getUserFromSettings(void* context, int /*id*/, const char** result, unsigned* /*len*/) -{ - if (context) { - *result = ((ConnectionSettings*) context)->username.c_str(); - QPID_LOG(debug, "getUserFromSettings(): " << (*result)); - return SASL_OK; - } else { - return SASL_FAIL; - } -} - -namespace { -// Global map of secrest allocated for SASL connections via callback -// to getPasswordFromSettings. Ensures secrets are freed. -class SecretsMap { - typedef std::map Map; - Map map; - public: - void keep(sasl_conn_t* conn, void* secret) { - Map::iterator i = map.find(conn); - if (i != map.end()) free(i->second); - map[conn] = secret; - } - - ~SecretsMap() { - for (Map::iterator i = map.begin(); i != map.end(); ++i) - free(i->second); - } -}; -SecretsMap getPasswordFromSettingsSecrets; -} - -int getPasswordFromSettings(sasl_conn_t* conn, void* context, int /*id*/, sasl_secret_t** psecret) -{ - if (context) { - size_t length = ((ConnectionSettings*) context)->password.size(); - sasl_secret_t* secret = (sasl_secret_t*) malloc(sizeof(sasl_secret_t) + length); - getPasswordFromSettingsSecrets.keep(conn, secret); - secret->len = length; - memcpy(secret->data, ((ConnectionSettings*) context)->password.data(), length); - *psecret = secret; - return SASL_OK; - } else { - return SASL_FAIL; - } -} - -}} // namespace qpid::client - -#endif diff --git a/cpp/src/qpid/client/SaslFactory.h b/cpp/src/qpid/client/SaslFactory.h deleted file mode 100644 index d012af06f7..0000000000 --- a/cpp/src/qpid/client/SaslFactory.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef QPID_CLIENT_SASLFACTORY_H -#define QPID_CLIENT_SASLFACTORY_H - -/* - * - * 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 "qpid/client/Sasl.h" -#include "qpid/sys/Mutex.h" -#include - -namespace qpid { -namespace client { - -/** - * Factory for instances of the Sasl interface through which Sasl - * support is provided to a ConnectionHandler. - */ -class SaslFactory -{ - public: - std::auto_ptr create(const ConnectionSettings&); - static SaslFactory& getInstance(); - ~SaslFactory(); - private: - SaslFactory(); - static qpid::sys::Mutex lock; - static std::auto_ptr instance; -}; -}} // namespace qpid::client - -#endif /*!QPID_CLIENT_SASLFACTORY_H*/ diff --git a/cpp/src/tests/sasl.mk b/cpp/src/tests/sasl.mk index 52cebe63f6..ae1666e891 100644 --- a/cpp/src/tests/sasl.mk +++ b/cpp/src/tests/sasl.mk @@ -26,6 +26,7 @@ cluster_authentication_soak_SOURCES=cluster_authentication_soak.cpp ForkedBroke cluster_authentication_soak_LDADD=$(lib_client) $(lib_broker) TESTS += run_cluster_authentication_test +TESTS += sasl_fed LONG_TESTS += run_cluster_authentication_soak endif # HAVE_SASL diff --git a/cpp/src/tests/sasl_fed b/cpp/src/tests/sasl_fed new file mode 100755 index 0000000000..550b5a1626 --- /dev/null +++ b/cpp/src/tests/sasl_fed @@ -0,0 +1,152 @@ +#! /bin/bash + +source test_env.sh + +minimum_sasl_version="2.1.22" +if [ ! `pkg-config --atleast-version $minimum_sasl_version cyrus-sasl`]; then + echo "sasl_fed requires at least $minimum_sasl_version" + exit 0 +fi + +let minimum_sasl_version=$((2 * 65536 + 1 * 256 + 22)) +sasl_version_numbers=(`rpm -q cyrus-sasl-devel | head -1 | tr '-' ' ' | awk '{print $4}' | tr '.' ' '`) +let sasl_version=$((${sasl_version_numbers[0]} * 65536 + ${sasl_version_numbers[1]} * 256 + ${sasl_version_numbers[2]})) + +if [ "$sasl_version" -lt "$minimum_sasl_version" ]; then + echo "sasl_fed requires version 2.1.22 or later" + exit 0 +fi + +exit + +QPID_SRC=$top_srcdir/src +QPIDD=$QPID_SRC/.libs/qpidd +PY_TOOLS=$QPID_TOOLS/src/py + +sasl_config_file=$QPID_SRC/tests/sasl_config + +my_random_number=$RANDOM +tmp_root=/tmp/sasl_fed/$my_random_number +mkdir -p $tmp_root + + +#-------------------------------------------------- +#echo " Starting broker 1" +#-------------------------------------------------- +$QPIDD \ + -p 0 \ + --data-dir $tmp_root/data_1 \ + --auth=yes \ + --mgmt-enable=yes \ + --log-enable info+ \ + --log-source yes \ + --log-to-file $tmp_root/qpidd_1.log \ + --sasl-config=$sasl_config_file \ + -d > $tmp_root/broker_1_port + +broker_1_port=`cat $tmp_root/broker_1_port` + + +#-------------------------------------------------- +#echo " Starting broker 2" +#-------------------------------------------------- +$QPIDD \ + -p 0 \ + --data-dir $tmp_root/data_2 \ + --auth=yes \ + --mgmt-enable=yes \ + --log-enable info+ \ + --log-source yes \ + --log-to-file $tmp_root/qpidd_2.log \ + --sasl-config=$sasl_config_file \ + -d > $tmp_root/broker_2_port + +broker_2_port=`cat $tmp_root/broker_2_port` + + +# Now find the PIDs so I can kill them later. +#pids=`ps -aef | grep -v grep | grep sasl_fed | grep $my_random_number | awk '{print $2}'` + + +# I am not randomizing these names, because the test creates its own brokers. +QUEUE_NAME=sasl_fed_queue +ROUTING_KEY=sasl_fed_queue +EXCHANGE_NAME=sasl_fedex + +#-------------------------------------------------- +#echo " add exchanges" +#-------------------------------------------------- +$PY_TOOLS/qpid-config -a localhost:$broker_1_port add exchange direct $EXCHANGE_NAME +$PY_TOOLS/qpid-config -a localhost:$broker_2_port add exchange direct $EXCHANGE_NAME + + +#-------------------------------------------------- +#echo " add queues" +#-------------------------------------------------- +$PY_TOOLS/qpid-config -a localhost:$broker_1_port add queue $QUEUE_NAME +$PY_TOOLS/qpid-config -a localhost:$broker_2_port add queue $QUEUE_NAME + +sleep 5 + +#-------------------------------------------------- +#echo " create bindings" +#-------------------------------------------------- +$PY_TOOLS/qpid-config -a localhost:$broker_1_port bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY +$PY_TOOLS/qpid-config -a localhost:$broker_2_port bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY + +sleep 5 + + +#-------------------------------------------------- +#echo " qpid-route route add" +#-------------------------------------------------- +$PY_TOOLS/qpid-route route add zag/zag@localhost:$broker_2_port zag/zag@localhost:$broker_1_port $EXCHANGE_NAME $ROUTING_KEY "" "" DIGEST-MD5 + +sleep 5 + + +n_messages=100 +#-------------------------------------------------- +#echo " Sending 100 messages to $broker_1_port " +#-------------------------------------------------- +$QPID_SRC/tests/datagen --count $n_messages | $QPID_SRC/tests/sender --username zag --password zag --exchange $EXCHANGE_NAME --routing-key $ROUTING_KEY --port $broker_1_port + +sleep 5 + +#-------------------------------------------------- +#echo " Examine Broker $broker_1_port" +#-------------------------------------------------- +broker_1_message_count=`$PY_TOOLS/qpid-stat -q localhost:$broker_1_port | grep sasl_fed_queue | awk '{print $2}'` +#echo " " + +#-------------------------------------------------- +#echo " Examine Broker $broker_2_port" +#-------------------------------------------------- +broker_2_message_count=`$PY_TOOLS/qpid-stat -q localhost:$broker_2_port | grep sasl_fed_queue | awk '{print $2}'` +#echo " " + +#-------------------------------------------------- +#echo " Asking brokers to quit." +#-------------------------------------------------- +$QPIDD --port $broker_1_port --quit +$QPIDD --port $broker_2_port --quit + + +#-------------------------------------------------- +#echo "Removing temporary directory $tmp_root" +#-------------------------------------------------- +rm -rf $tmp_root + +if [ "$broker_2_message_count" = "$n_messages" ]; then + echo "good: $broker_2_message_count" + exit 0 +else + echo "not ideal: $broker_1_message_count != $n_messages" + exit 1 +fi + + + + + + -- cgit v1.2.1