summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2010-11-07 14:58:23 (GMT)
committerRemko Tronçon <git@el-tramo.be>2010-11-07 18:04:57 (GMT)
commite2f2e48f6e01739ccaa763ff7f037306131d4e61 (patch)
tree92fefe8ff9255356d849d1eadcad45666bde52e5 /Swiften
parent832d109bfabc16ef2834790743c1d235b254d781 (diff)
downloadswift-e2f2e48f6e01739ccaa763ff7f037306131d4e61.zip
swift-e2f2e48f6e01739ccaa763ff7f037306131d4e61.tar.bz2
Added security error handling to Swiften.
Diffstat (limited to 'Swiften')
-rw-r--r--Swiften/Client/ClientSession.cpp19
-rw-r--r--Swiften/Client/ClientSession.h5
-rw-r--r--Swiften/Client/CoreClient.cpp17
-rw-r--r--Swiften/Client/CoreClient.h32
-rw-r--r--Swiften/Client/UnitTest/ClientSessionTest.cpp8
-rw-r--r--Swiften/Component/UnitTest/ComponentSessionTest.cpp8
-rw-r--r--Swiften/Session/BasicSessionStream.cpp9
-rw-r--r--Swiften/Session/BasicSessionStream.h2
-rw-r--r--Swiften/Session/SessionStream.h5
-rw-r--r--Swiften/TLS/CertificateVerificationError.h9
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.cpp65
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.h2
-rw-r--r--Swiften/TLS/SConscript1
13 files changed, 179 insertions, 3 deletions
diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp
index 4c2be95..7170a20 100644
--- a/Swiften/Client/ClientSession.cpp
+++ b/Swiften/Client/ClientSession.cpp
@@ -11,6 +11,7 @@
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>
+#include "Swiften/TLS/SecurityError.h"
#include "Swiften/Elements/ProtocolHeader.h"
#include "Swiften/Elements/StreamFeatures.h"
#include "Swiften/Elements/StartTLSRequest.h"
@@ -322,8 +323,26 @@ void ClientSession::sendCredentials(const String& password) {
stream->writeElement(boost::shared_ptr<AuthRequest>(new AuthRequest(authenticator->getName(), authenticator->getResponse())));
}
+void ClientSession::continueAfterSecurityError() {
+ checkState(WaitingForContinueAfterSecurityError);
+ continueAfterTLSEncrypted();
+}
+
void ClientSession::handleTLSEncrypted() {
checkState(Encrypting);
+
+ Certificate::ref certificate = stream->getPeerCertificate();
+ boost::optional<CertificateVerificationError> verificationError = stream->getPeerCertificateVerificationError();
+ if (verificationError) {
+ state = WaitingForContinueAfterSecurityError;
+ onSecurityError(SecurityError(*verificationError));
+ }
+ else {
+ continueAfterTLSEncrypted();
+ }
+}
+
+void ClientSession::continueAfterTLSEncrypted() {
state = WaitingForStreamStart;
stream->resetXMPPParser();
sendStreamHeader();
diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h
index 83744e0..b14a6ec 100644
--- a/Swiften/Client/ClientSession.h
+++ b/Swiften/Client/ClientSession.h
@@ -20,6 +20,7 @@
namespace Swift {
class ClientAuthenticator;
+ class SecurityError;
class ClientSession : public boost::enable_shared_from_this<ClientSession> {
public:
@@ -30,6 +31,7 @@ namespace Swift {
Compressing,
WaitingForEncrypt,
Encrypting,
+ WaitingForContinueAfterSecurityError,
WaitingForCredentials,
Authenticating,
EnablingSessionManagement,
@@ -81,9 +83,11 @@ namespace Swift {
void sendCredentials(const String& password);
void sendStanza(boost::shared_ptr<Stanza>);
+ void continueAfterSecurityError();
public:
boost::signal<void ()> onNeedCredentials;
+ boost::signal<void (const SecurityError&)> onSecurityError;
boost::signal<void ()> onInitialized;
boost::signal<void (boost::shared_ptr<Swift::Error>)> onFinished;
boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaReceived;
@@ -115,6 +119,7 @@ namespace Swift {
void requestAck();
void handleStanzaAcked(boost::shared_ptr<Stanza> stanza);
void ack(unsigned int handledStanzasCount);
+ void continueAfterTLSEncrypted();
private:
JID localJID;
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp
index 214e6b1..4202483 100644
--- a/Swiften/Client/CoreClient.cpp
+++ b/Swiften/Client/CoreClient.cpp
@@ -23,7 +23,7 @@
namespace Swift {
-CoreClient::CoreClient(EventLoop* eventLoop, const JID& jid, const String& password) : resolver_(eventLoop), jid_(jid), password_(password), eventLoop(eventLoop), disconnectRequested_(false) {
+CoreClient::CoreClient(EventLoop* eventLoop, const JID& jid, const String& password) : resolver_(eventLoop), jid_(jid), password_(password), eventLoop(eventLoop), disconnectRequested_(false), ignoreSecurityErrors(true) {
stanzaChannel_ = new ClientSessionStanzaChannel();
stanzaChannel_->onMessageReceived.connect(boost::ref(onMessageReceived));
stanzaChannel_->onPresenceReceived.connect(boost::ref(onPresenceReceived));
@@ -93,6 +93,7 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connectio
stanzaChannel_->setSession(session_);
session_->onFinished.connect(boost::bind(&CoreClient::handleSessionFinished, this, _1));
session_->onNeedCredentials.connect(boost::bind(&CoreClient::handleNeedCredentials, this));
+ session_->onSecurityError.connect(boost::bind(&CoreClient::handleSecurityError, this, _1));
session_->start();
}
}
@@ -114,6 +115,7 @@ void CoreClient::setCertificate(const String& certificate) {
}
void CoreClient::handleSessionFinished(boost::shared_ptr<Error> error) {
+ session_->onSecurityError.disconnect(boost::bind(&CoreClient::handleSecurityError, this, _1));
session_->onFinished.disconnect(boost::bind(&CoreClient::handleSessionFinished, this, _1));
session_->onNeedCredentials.disconnect(boost::bind(&CoreClient::handleNeedCredentials, this));
session_.reset();
@@ -214,4 +216,17 @@ bool CoreClient::isActive() const {
return session_ || connector_;
}
+void CoreClient::handleSecurityError(const SecurityError& error) {
+ if (ignoreSecurityErrors) {
+ session_->continueAfterSecurityError();
+ }
+ else {
+ onSecurityError(error);
+ }
+}
+
+void CoreClient::continueAfterSecurityError() {
+ session_->continueAfterSecurityError();
+}
+
}
diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h
index 4170e8d..3176a51 100644
--- a/Swiften/Client/CoreClient.h
+++ b/Swiften/Client/CoreClient.h
@@ -32,6 +32,7 @@ namespace Swift {
class ClientSession;
class BasicSessionStream;
class EventLoop;
+ class SecurityError;
/**
* The central class for communicating with an XMPP server.
@@ -71,6 +72,14 @@ namespace Swift {
void connect(const String& host);
/**
+ * Instructs the client to continue initializing the session
+ * after a security error has occurred (and as such ignoring the error)
+ *
+ * \see onSecurityError
+ */
+ void continueAfterSecurityError();
+
+ /**
* Sends a message.
*/
void sendMessage(Message::ref);
@@ -131,8 +140,29 @@ namespace Swift {
return stanzaChannel_;
}
+ /**
+ * Sets whether security errors should be ignored or not.
+ *
+ * If this is set to 'true', onSecurityError will not be called when a security
+ * error occurs, and connecting will continue.
+ *
+ * Defaults to true.
+ */
+ void setIgnoreSecurityErrors(bool b) {
+ ignoreSecurityErrors = b;
+ }
+
public:
/**
+ * Emitted when an error occurred while establishing a secure connection.
+ *
+ * If the error is to be ignored, call continueAfterSecurityError(), otherwise call
+ * finish().
+ * This signal is not emitted when setIgnoreSecurityErrors() is set to true.
+ */
+ boost::signal<void (const SecurityError&)> onSecurityError;
+
+ /**
* Emitted when the client was disconnected from the network.
*
* If the connection was due to a non-recoverable error, the type
@@ -187,6 +217,7 @@ namespace Swift {
void handleNeedCredentials();
void handleDataRead(const String&);
void handleDataWritten(const String&);
+ void handleSecurityError(const SecurityError& securityError);
private:
PlatformDomainNameResolver resolver_;
@@ -206,5 +237,6 @@ namespace Swift {
boost::shared_ptr<ClientSession> session_;
String certificate_;
bool disconnectRequested_;
+ bool ignoreSecurityErrors;
};
}
diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp
index 2cd9fd2..43a8bf3 100644
--- a/Swiften/Client/UnitTest/ClientSessionTest.cpp
+++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp
@@ -283,6 +283,14 @@ class ClientSessionTest : public CppUnit::TestFixture {
return tlsEncrypted;
}
+ virtual Certificate::ref getPeerCertificate() const {
+ return Certificate::ref();
+ }
+
+ virtual boost::optional<CertificateVerificationError> getPeerCertificateVerificationError() const {
+ return boost::optional<CertificateVerificationError>();
+ }
+
virtual void addZLibCompression() {
compressed = true;
}
diff --git a/Swiften/Component/UnitTest/ComponentSessionTest.cpp b/Swiften/Component/UnitTest/ComponentSessionTest.cpp
index d35a664..b6b57dd 100644
--- a/Swiften/Component/UnitTest/ComponentSessionTest.cpp
+++ b/Swiften/Component/UnitTest/ComponentSessionTest.cpp
@@ -123,6 +123,14 @@ class ComponentSessionTest : public CppUnit::TestFixture {
return false;
}
+ virtual Certificate::ref getPeerCertificate() const {
+ return Certificate::ref();
+ }
+
+ virtual boost::optional<CertificateVerificationError> getPeerCertificateVerificationError() const {
+ return boost::optional<CertificateVerificationError>();
+ }
+
virtual void addZLibCompression() {
assert(false);
}
diff --git a/Swiften/Session/BasicSessionStream.cpp b/Swiften/Session/BasicSessionStream.cpp
index a4b1c84..65a241c 100644
--- a/Swiften/Session/BasicSessionStream.cpp
+++ b/Swiften/Session/BasicSessionStream.cpp
@@ -85,6 +85,15 @@ bool BasicSessionStream::isTLSEncrypted() {
return tlsLayer;
}
+Certificate::ref BasicSessionStream::getPeerCertificate() const {
+ return tlsLayer->getPeerCertificate();
+}
+
+boost::optional<CertificateVerificationError> BasicSessionStream::getPeerCertificateVerificationError() const {
+ return tlsLayer->getPeerCertificateVerificationError();
+}
+
+
void BasicSessionStream::addZLibCompression() {
boost::shared_ptr<CompressionLayer> compressionLayer(new CompressionLayer());
streamStack->addLayer(compressionLayer);
diff --git a/Swiften/Session/BasicSessionStream.h b/Swiften/Session/BasicSessionStream.h
index 22620be..8addeb6 100644
--- a/Swiften/Session/BasicSessionStream.h
+++ b/Swiften/Session/BasicSessionStream.h
@@ -52,6 +52,8 @@ namespace Swift {
virtual bool supportsTLSEncryption();
virtual void addTLSEncryption();
virtual bool isTLSEncrypted();
+ virtual Certificate::ref getPeerCertificate() const;
+ virtual boost::optional<CertificateVerificationError> getPeerCertificateVerificationError() const;
virtual void setWhitespacePingEnabled(bool);
diff --git a/Swiften/Session/SessionStream.h b/Swiften/Session/SessionStream.h
index b2bf9a6..1bf9090 100644
--- a/Swiften/Session/SessionStream.h
+++ b/Swiften/Session/SessionStream.h
@@ -8,11 +8,14 @@
#include "Swiften/Base/boost_bsignals.h"
#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
#include "Swiften/Elements/ProtocolHeader.h"
#include "Swiften/Elements/Element.h"
#include "Swiften/Base/Error.h"
#include "Swiften/TLS/PKCS12Certificate.h"
+#include "Swiften/TLS/Certificate.h"
+#include "Swiften/TLS/CertificateVerificationError.h"
namespace Swift {
class SessionStream {
@@ -57,6 +60,8 @@ namespace Swift {
return !certificate.isNull();
}
+ virtual Certificate::ref getPeerCertificate() const = 0;
+ virtual boost::optional<CertificateVerificationError> getPeerCertificateVerificationError() const = 0;
boost::signal<void (const ProtocolHeader&)> onStreamStartReceived;
boost::signal<void (boost::shared_ptr<Element>)> onElementReceived;
diff --git a/Swiften/TLS/CertificateVerificationError.h b/Swiften/TLS/CertificateVerificationError.h
index 71895ff..76b4aff 100644
--- a/Swiften/TLS/CertificateVerificationError.h
+++ b/Swiften/TLS/CertificateVerificationError.h
@@ -11,6 +11,15 @@ namespace Swift {
public:
enum Type {
UnknownError,
+ Expired,
+ NotYetValid,
+ SelfSigned,
+ Rejected,
+ Untrusted,
+ InvalidPurpose,
+ PathLengthExceeded,
+ InvalidSignature,
+ InvalidCA,
};
CertificateVerificationError(Type type = UnknownError) : type(type) {}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
index 234c831..c78d5a1 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
@@ -221,13 +221,74 @@ Certificate::ref OpenSSLContext::getPeerCertificate() const {
}
boost::optional<CertificateVerificationError> OpenSSLContext::getPeerCertificateVerificationError() const {
- long verifyResult = SSL_get_verify_result(handle_);
+ int verifyResult = SSL_get_verify_result(handle_);
if (verifyResult != X509_V_OK) {
- return CertificateVerificationError();
+ return CertificateVerificationError(getVerificationErrorTypeForResult(verifyResult));
}
else {
return boost::optional<CertificateVerificationError>();
}
}
+CertificateVerificationError::Type OpenSSLContext::getVerificationErrorTypeForResult(int result) {
+ assert(result != 0);
+ switch (result) {
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ return CertificateVerificationError::NotYetValid;
+
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ return CertificateVerificationError::Expired;
+
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ return CertificateVerificationError::SelfSigned;
+
+ case X509_V_ERR_CERT_UNTRUSTED:
+ return CertificateVerificationError::Untrusted;
+
+ case X509_V_ERR_CERT_REJECTED:
+ return CertificateVerificationError::Rejected;
+
+ case X509_V_ERR_INVALID_PURPOSE:
+ return CertificateVerificationError::InvalidPurpose;
+
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ return CertificateVerificationError::PathLengthExceeded;
+
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ return CertificateVerificationError::InvalidSignature;
+
+ case X509_V_ERR_INVALID_CA:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ return CertificateVerificationError::InvalidCA;
+
+ case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+ case X509_V_ERR_AKID_SKID_MISMATCH:
+ case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+ case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+ return CertificateVerificationError::UnknownError;
+
+ // Unused / should not happen
+ case X509_V_ERR_CERT_REVOKED:
+ case X509_V_ERR_OUT_OF_MEM:
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ case X509_V_ERR_APPLICATION_VERIFICATION:
+ default:
+ return CertificateVerificationError::UnknownError;
+ }
+}
+
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h
index a0e73c4..31141a5 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h
@@ -33,6 +33,8 @@ namespace Swift {
private:
static void ensureLibraryInitialized();
+ static CertificateVerificationError::Type getVerificationErrorTypeForResult(int);
+
void doConnect();
void sendPendingDataToNetwork();
void sendPendingDataToApplication();
diff --git a/Swiften/TLS/SConscript b/Swiften/TLS/SConscript
index 6a67545..b84dbc0 100644
--- a/Swiften/TLS/SConscript
+++ b/Swiften/TLS/SConscript
@@ -3,6 +3,7 @@ Import("swiften_env")
objects = swiften_env.StaticObject([
"TLSContext.cpp",
"TLSContextFactory.cpp",
+ "SecurityError.cpp",
])
if swiften_env.get("HAVE_OPENSSL", 0) :