diff options
| author | Remko Tronçon <git@el-tramo.be> | 2012-06-18 19:00:13 (GMT) |
|---|---|---|
| committer | Remko Tronçon <git@el-tramo.be> | 2012-06-18 19:00:13 (GMT) |
| commit | 6080dd4915801b45598268c805b62aa6c723a3a3 (patch) | |
| tree | 56ab5acd5ce2e354eff3a276f5acf87e8da0571f | |
| parent | 7108ff51d73e4e0b38ab2acd139dca90ff4218f4 (diff) | |
| download | swift-contrib-6080dd4915801b45598268c805b62aa6c723a3a3.zip swift-contrib-6080dd4915801b45598268c805b62aa6c723a3a3.tar.bz2 | |
Handle unexpected challenges.
Resolves: #1132
| -rw-r--r-- | Swiften/Client/ClientSession.cpp | 34 | ||||
| -rw-r--r-- | Swiften/Client/UnitTest/ClientSessionTest.cpp | 41 | ||||
| -rw-r--r-- | Swiften/SASL/EXTERNALClientAuthenticator.cpp | 26 | ||||
| -rw-r--r-- | Swiften/SASL/EXTERNALClientAuthenticator.h | 23 | ||||
| -rw-r--r-- | Swiften/SASL/SConscript | 1 |
5 files changed, 111 insertions, 14 deletions
diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp index 7e1f517..48e38b9 100644 --- a/Swiften/Client/ClientSession.cpp +++ b/Swiften/Client/ClientSession.cpp @@ -31,29 +31,33 @@ #include <Swiften/Elements/EnableStreamManagement.h> #include <Swiften/Elements/StreamManagementEnabled.h> #include <Swiften/Elements/StreamManagementFailed.h> #include <Swiften/Elements/StartSession.h> #include <Swiften/Elements/StanzaAck.h> #include <Swiften/Elements/StanzaAckRequest.h> #include <Swiften/Elements/IQ.h> #include <Swiften/Elements/ResourceBind.h> #include <Swiften/SASL/PLAINClientAuthenticator.h> +#include <Swiften/SASL/EXTERNALClientAuthenticator.h> #include <Swiften/SASL/SCRAMSHA1ClientAuthenticator.h> #include <Swiften/SASL/DIGESTMD5ClientAuthenticator.h> #include <Swiften/Session/SessionStream.h> #include <Swiften/TLS/CertificateTrustChecker.h> #include <Swiften/TLS/ServerIdentityVerifier.h> #include <Swiften/Base/Log.h> #ifdef SWIFTEN_PLATFORM_WIN32 #include <Swiften/Base/WindowsRegistry.h> #endif +#define CHECK_STATE_OR_RETURN(a) \ + if (!checkState(a)) { return; } + namespace Swift { ClientSession::ClientSession( const JID& jid, boost::shared_ptr<SessionStream> stream) : localJID(jid), state(Initial), stream(stream), allowPLAINOverNonTLS(false), @@ -95,19 +99,19 @@ void ClientSession::sendStreamHeader() { void ClientSession::sendStanza(boost::shared_ptr<Stanza> stanza) { stream->writeElement(stanza); if (stanzaAckRequester_) { stanzaAckRequester_->handleStanzaSent(stanza); } } void ClientSession::handleStreamStart(const ProtocolHeader&) { - checkState(WaitingForStreamStart); + CHECK_STATE_OR_RETURN(WaitingForStreamStart); state = Negotiating; } void ClientSession::handleElement(boost::shared_ptr<Element> element) { if (boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(element)) { if (stanzaAckResponder_) { stanzaAckResponder_->handleStanzaReceived(); } if (getState() == Initialized) { @@ -176,44 +180,44 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(element); if (stanza) { if (stanzaAckResponder_) { stanzaAckResponder_->handleStanzaReceived(); } onStanzaReceived(stanza); } } else if (StreamFeatures* streamFeatures = dynamic_cast<StreamFeatures*>(element.get())) { - if (!checkState(Negotiating)) { - return; - } + CHECK_STATE_OR_RETURN(Negotiating); if (streamFeatures->hasStartTLS() && stream->supportsTLSEncryption() && useTLS != NeverUseTLS) { state = WaitingForEncrypt; stream->writeElement(boost::make_shared<StartTLSRequest>()); } else if (useTLS == RequireTLS && !stream->isTLSEncrypted()) { finishSession(Error::NoSupportedAuthMechanismsError); } else if (useStreamCompression && stream->supportsZLibCompression() && streamFeatures->hasCompressionMethod("zlib")) { state = Compressing; stream->writeElement(boost::make_shared<CompressRequest>("zlib")); } else if (streamFeatures->hasAuthenticationMechanisms()) { if (stream->hasTLSCertificate()) { if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) { + authenticator = new EXTERNALClientAuthenticator(); state = Authenticating; stream->writeElement(boost::make_shared<AuthRequest>("EXTERNAL", createSafeByteArray(""))); } else { finishSession(Error::TLSClientCertificateError); } } else if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) { + authenticator = new EXTERNALClientAuthenticator(); state = Authenticating; stream->writeElement(boost::make_shared<AuthRequest>("EXTERNAL", createSafeByteArray(""))); } else if (streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1") || streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1-PLUS")) { std::ostringstream s; ByteArray finishMessage; bool plus = stream->isTLSEncrypted() && streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1-PLUS"); if (plus) { finishMessage = stream->getTLSFinishMessage(); @@ -256,19 +260,19 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { // Resource binding is a MUST finishSession(Error::ResourceBindError); } else { continueSessionInitialization(); } } } else if (boost::dynamic_pointer_cast<Compressed>(element)) { - checkState(Compressing); + CHECK_STATE_OR_RETURN(Compressing); state = WaitingForStreamStart; stream->addZLibCompression(); stream->resetXMPPParser(); sendStreamHeader(); } else if (boost::dynamic_pointer_cast<CompressFailure>(element)) { finishSession(Error::CompressionFailedError); } else if (boost::dynamic_pointer_cast<StreamManagementEnabled>(element)) { @@ -279,49 +283,46 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { stanzaAckResponder_->onAck.connect(boost::bind(&ClientSession::ack, shared_from_this(), _1)); needAcking = false; continueSessionInitialization(); } else if (boost::dynamic_pointer_cast<StreamManagementFailed>(element)) { needAcking = false; continueSessionInitialization(); } else if (AuthChallenge* challenge = dynamic_cast<AuthChallenge*>(element.get())) { - checkState(Authenticating); + CHECK_STATE_OR_RETURN(Authenticating); assert(authenticator); if (authenticator->setChallenge(challenge->getValue())) { stream->writeElement(boost::make_shared<AuthResponse>(authenticator->getResponse())); } else { finishSession(Error::AuthenticationFailedError); } } else if (AuthSuccess* authSuccess = dynamic_cast<AuthSuccess*>(element.get())) { - checkState(Authenticating); - if (authenticator && !authenticator->setChallenge(authSuccess->getValue())) { - delete authenticator; - authenticator = NULL; + CHECK_STATE_OR_RETURN(Authenticating); + assert(authenticator); + if (!authenticator->setChallenge(authSuccess->getValue())) { finishSession(Error::ServerVerificationFailedError); } else { state = WaitingForStreamStart; delete authenticator; authenticator = NULL; stream->resetXMPPParser(); sendStreamHeader(); } } else if (dynamic_cast<AuthFailure*>(element.get())) { - delete authenticator; - authenticator = NULL; finishSession(Error::AuthenticationFailedError); } else if (dynamic_cast<TLSProceed*>(element.get())) { - checkState(WaitingForEncrypt); + CHECK_STATE_OR_RETURN(WaitingForEncrypt); state = Encrypting; stream->addTLSEncryption(); } else if (dynamic_cast<StartTLSFailure*>(element.get())) { finishSession(Error::TLSError); } else { // FIXME Not correct? state = Initialized; @@ -356,25 +357,26 @@ bool ClientSession::checkState(State state) { if (this->state != state) { finishSession(Error::UnexpectedElementError); return false; } return true; } void ClientSession::sendCredentials(const SafeByteArray& password) { assert(WaitingForCredentials); + assert(authenticator); state = Authenticating; authenticator->setCredentials(localJID.getNode(), password); stream->writeElement(boost::make_shared<AuthRequest>(authenticator->getName(), authenticator->getResponse())); } void ClientSession::handleTLSEncrypted() { - checkState(Encrypting); + CHECK_STATE_OR_RETURN(Encrypting); std::vector<Certificate::ref> certificateChain = stream->getPeerCertificateChain(); boost::shared_ptr<CertificateVerificationError> verificationError = stream->getPeerCertificateVerificationError(); if (verificationError) { checkTrustOrFinish(certificateChain, verificationError); } else { ServerIdentityVerifier identityVerifier(localJID); if (!certificateChain.empty() && identityVerifier.certificateVerifies(certificateChain[0])) { @@ -442,18 +444,22 @@ void ClientSession::finishSession(boost::shared_ptr<Swift::Error> error) { error_ = error; } else { SWIFT_LOG(warning) << "Session finished twice"; } assert(stream->isOpen()); if (stanzaAckResponder_) { stanzaAckResponder_->handleAckRequestReceived(); } + if (authenticator) { + delete authenticator; + authenticator = NULL; + } stream->writeFooter(); stream->close(); } void ClientSession::requestAck() { stream->writeElement(boost::make_shared<StanzaAckRequest>()); } void ClientSession::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) { diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp index d1ca70a..a8cd53c 100644 --- a/Swiften/Client/UnitTest/ClientSessionTest.cpp +++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp @@ -8,18 +8,19 @@ #include <cppunit/extensions/TestFactoryRegistry.h> #include <deque> #include <boost/bind.hpp> #include <boost/optional.hpp> #include <boost/smart_ptr/make_shared.hpp> #include <Swiften/Session/SessionStream.h> #include <Swiften/Client/ClientSession.h> #include <Swiften/Elements/Message.h> +#include <Swiften/Elements/AuthChallenge.h> #include <Swiften/Elements/StartTLSRequest.h> #include <Swiften/Elements/StreamFeatures.h> #include <Swiften/Elements/StreamError.h> #include <Swiften/Elements/TLSProceed.h> #include <Swiften/Elements/StartTLSFailure.h> #include <Swiften/Elements/AuthRequest.h> #include <Swiften/Elements/AuthSuccess.h> #include <Swiften/Elements/AuthFailure.h> #include <Swiften/Elements/StreamManagementEnabled.h> @@ -41,20 +42,22 @@ class ClientSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testStartTLS_ServerError); CPPUNIT_TEST(testStartTLS_ConnectError); CPPUNIT_TEST(testStartTLS_InvalidIdentity); CPPUNIT_TEST(testStart_StreamFeaturesWithoutResourceBindingFails); CPPUNIT_TEST(testAuthenticate); CPPUNIT_TEST(testAuthenticate_Unauthorized); CPPUNIT_TEST(testAuthenticate_NoValidAuthMechanisms); CPPUNIT_TEST(testAuthenticate_PLAINOverNonTLS); CPPUNIT_TEST(testAuthenticate_RequireTLS); + CPPUNIT_TEST(testAuthenticate_EXTERNAL); CPPUNIT_TEST(testStreamManagement); CPPUNIT_TEST(testStreamManagement_Failed); + CPPUNIT_TEST(testUnexpectedChallenge); CPPUNIT_TEST(testFinishAcksStanzas); /* CPPUNIT_TEST(testResourceBind); CPPUNIT_TEST(testResourceBind_ChangeResource); CPPUNIT_TEST(testResourceBind_EmptyResource); CPPUNIT_TEST(testResourceBind_Error); CPPUNIT_TEST(testSessionStart); CPPUNIT_TEST(testSessionStart_Error); CPPUNIT_TEST(testSessionStart_AfterResourceBind); @@ -241,18 +244,46 @@ class ClientSessionTest : public CppUnit::TestFixture { server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithUnknownAuthentication(); CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } + void testAuthenticate_EXTERNAL() { + boost::shared_ptr<ClientSession> session(createSession()); + session->start(); + server->receiveStreamStart(); + server->sendStreamStart(); + server->sendStreamFeaturesWithEXTERNALAuthentication(); + server->receiveAuthRequest("EXTERNAL"); + server->sendAuthSuccess(); + server->receiveStreamStart(); + + session->finish(); + } + + void testUnexpectedChallenge() { + boost::shared_ptr<ClientSession> session(createSession()); + session->start(); + server->receiveStreamStart(); + server->sendStreamStart(); + server->sendStreamFeaturesWithEXTERNALAuthentication(); + server->receiveAuthRequest("EXTERNAL"); + server->sendChallenge(); + server->sendChallenge(); + + CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); + CPPUNIT_ASSERT(sessionFinishedReceived); + CPPUNIT_ASSERT(sessionFinishedError); + } + void testStreamManagement() { boost::shared_ptr<ClientSession> session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithPLAINAuthentication(); session->sendCredentials(createSafeByteArray("mypass")); server->receiveAuthRequest("PLAIN"); server->sendAuthSuccess(); @@ -438,18 +469,22 @@ class ClientSessionTest : public CppUnit::TestFixture { return onStreamStartReceived(header); } void sendStreamFeaturesWithStartTLS() { boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures()); streamFeatures->setHasStartTLS(); onElementReceived(streamFeatures); } + void sendChallenge() { + onElementReceived(boost::make_shared<AuthChallenge>()); + } + void sendStreamError() { onElementReceived(boost::make_shared<StreamError>()); } void sendTLSProceed() { onElementReceived(boost::make_shared<TLSProceed>()); } void sendTLSFailure() { @@ -464,18 +499,24 @@ class ClientSessionTest : public CppUnit::TestFixture { onElementReceived(streamFeatures); } void sendStreamFeaturesWithPLAINAuthentication() { boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures()); streamFeatures->addAuthenticationMechanism("PLAIN"); onElementReceived(streamFeatures); } + void sendStreamFeaturesWithEXTERNALAuthentication() { + boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures()); + streamFeatures->addAuthenticationMechanism("EXTERNAL"); + onElementReceived(streamFeatures); + } + void sendStreamFeaturesWithUnknownAuthentication() { boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures()); streamFeatures->addAuthenticationMechanism("UNKNOWN"); onElementReceived(streamFeatures); } void sendStreamFeaturesWithBindAndStreamManagement() { boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures()); streamFeatures->setHasResourceBind(); diff --git a/Swiften/SASL/EXTERNALClientAuthenticator.cpp b/Swiften/SASL/EXTERNALClientAuthenticator.cpp new file mode 100644 index 0000000..a3016d1 --- /dev/null +++ b/Swiften/SASL/EXTERNALClientAuthenticator.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2012 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/SASL/EXTERNALClientAuthenticator.h> + +namespace Swift { + +EXTERNALClientAuthenticator::EXTERNALClientAuthenticator() : ClientAuthenticator("EXTERNAL"), finished(false) { +} + +boost::optional<SafeByteArray> EXTERNALClientAuthenticator::getResponse() const { + return boost::optional<SafeByteArray>(); +} + +bool EXTERNALClientAuthenticator::setChallenge(const boost::optional<ByteArray>&) { + if (finished) { + return false; + } + finished = true; + return true; +} + +} diff --git a/Swiften/SASL/EXTERNALClientAuthenticator.h b/Swiften/SASL/EXTERNALClientAuthenticator.h new file mode 100644 index 0000000..b986295 --- /dev/null +++ b/Swiften/SASL/EXTERNALClientAuthenticator.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/SASL/ClientAuthenticator.h> +#include <Swiften/Base/ByteArray.h> + +namespace Swift { + class EXTERNALClientAuthenticator : public ClientAuthenticator { + public: + EXTERNALClientAuthenticator(); + + virtual boost::optional<SafeByteArray> getResponse() const; + virtual bool setChallenge(const boost::optional<ByteArray>&); + + private: + bool finished; + }; +} diff --git a/Swiften/SASL/SConscript b/Swiften/SASL/SConscript index 3a67938..6509547 100644 --- a/Swiften/SASL/SConscript +++ b/Swiften/SASL/SConscript @@ -1,15 +1,16 @@ Import("swiften_env", "env") myenv = swiften_env.Clone() objects = myenv.SwiftenObject([ "ClientAuthenticator.cpp", + "EXTERNALClientAuthenticator.cpp", "PLAINClientAuthenticator.cpp", "PLAINMessage.cpp", "SCRAMSHA1ClientAuthenticator.cpp", "DIGESTMD5Properties.cpp", "DIGESTMD5ClientAuthenticator.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) env.Append(UNITTEST_SOURCES = [ |
Swift