summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swiften/Client/ClientSession.cpp34
-rw-r--r--Swiften/Client/UnitTest/ClientSessionTest.cpp41
-rw-r--r--Swiften/SASL/EXTERNALClientAuthenticator.cpp26
-rw-r--r--Swiften/SASL/EXTERNALClientAuthenticator.h23
-rw-r--r--Swiften/SASL/SConscript1
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 = [