diff options
author | Michael Goulish <mgoulish@apache.org> | 2010-10-20 08:03:36 +0000 |
---|---|---|
committer | Michael Goulish <mgoulish@apache.org> | 2010-10-20 08:03:36 +0000 |
commit | bcb149706cdace4a333a811969e473451d9ab331 (patch) | |
tree | 6ad1e5797a8696968b91bdcf511eeac4bf4cb54f /cpp/src/qpid/client | |
parent | 346e5a55b9152ab603bf8b15bd7718beb9d6ff76 (diff) | |
download | qpid-python-bcb149706cdace4a333a811969e473451d9ab331.tar.gz |
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
Diffstat (limited to 'cpp/src/qpid/client')
-rw-r--r-- | cpp/src/qpid/client/ConnectionHandler.cpp | 10 | ||||
-rw-r--r-- | cpp/src/qpid/client/ConnectionHandler.h | 2 | ||||
-rw-r--r-- | cpp/src/qpid/client/Sasl.h | 64 | ||||
-rw-r--r-- | cpp/src/qpid/client/SaslFactory.cpp | 385 | ||||
-rw-r--r-- | cpp/src/qpid/client/SaslFactory.h | 48 |
5 files changed, 9 insertions, 500 deletions
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 <memory> -#include <string> -#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<qpid::sys::SecurityLayer> 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 <map> -#include <string.h> - -#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<SaslFactory>(new SaslFactory()); - } - return *instance; -} - -std::auto_ptr<Sasl> SaslFactory::create(const ConnectionSettings&) -{ - return std::auto_ptr<Sasl>(); -} - -qpid::sys::Mutex SaslFactory::lock; -std::auto_ptr<SaslFactory> 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 <sasl/sasl.h> -#include <strings.h> - -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<SecurityLayer> 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> 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<SaslFactory>(new SaslFactory()); - } - return *instance; -} - -std::auto_ptr<Sasl> SaslFactory::create(const ConnectionSettings& settings) -{ - std::auto_ptr<Sasl> 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<SecurityLayer> 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<const unsigned*>(value)); - std::auto_ptr<SecurityLayer> securityLayer; - if (ssf) { - QPID_LOG(info, "Installing security layer, SSF: "<< ssf); - securityLayer = std::auto_ptr<SecurityLayer>(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<sasl_conn_t*, void*> 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 <memory> - -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<Sasl> create(const ConnectionSettings&); - static SaslFactory& getInstance(); - ~SaslFactory(); - private: - SaslFactory(); - static qpid::sys::Mutex lock; - static std::auto_ptr<SaslFactory> instance; -}; -}} // namespace qpid::client - -#endif /*!QPID_CLIENT_SASLFACTORY_H*/ |