From e2f2e48f6e01739ccaa763ff7f037306131d4e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Sun, 7 Nov 2010 15:58:23 +0100 Subject: Added security error handling to Swiften. 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 #include +#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(new AuthRequest(authenticator->getName(), authenticator->getResponse()))); } +void ClientSession::continueAfterSecurityError() { + checkState(WaitingForContinueAfterSecurityError); + continueAfterTLSEncrypted(); +} + void ClientSession::handleTLSEncrypted() { checkState(Encrypting); + + Certificate::ref certificate = stream->getPeerCertificate(); + boost::optional 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 { 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); + void continueAfterSecurityError(); public: boost::signal onNeedCredentials; + boost::signal onSecurityError; boost::signal onInitialized; boost::signal)> onFinished; boost::signal)> onStanzaReceived; @@ -115,6 +119,7 @@ namespace Swift { void requestAck(); void handleStanzaAcked(boost::shared_ptr 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 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) { + 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 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 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 getPeerCertificateVerificationError() const { + return boost::optional(); + } + 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 getPeerCertificateVerificationError() const { + return boost::optional(); + } + 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 BasicSessionStream::getPeerCertificateVerificationError() const { + return tlsLayer->getPeerCertificateVerificationError(); +} + + void BasicSessionStream::addZLibCompression() { boost::shared_ptr 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 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 +#include #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 getPeerCertificateVerificationError() const = 0; boost::signal onStreamStartReceived; boost::signal)> 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 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::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) : -- cgit v0.10.2-6-g49f6