diff options
| -rw-r--r-- | Swiften/Client/ClientSession.cpp | 19 | ||||
| -rw-r--r-- | Swiften/Client/ClientSession.h | 5 | ||||
| -rw-r--r-- | Swiften/Client/CoreClient.cpp | 17 | ||||
| -rw-r--r-- | Swiften/Client/CoreClient.h | 32 | ||||
| -rw-r--r-- | Swiften/Client/UnitTest/ClientSessionTest.cpp | 8 | ||||
| -rw-r--r-- | Swiften/Component/UnitTest/ComponentSessionTest.cpp | 8 | ||||
| -rw-r--r-- | Swiften/Session/BasicSessionStream.cpp | 9 | ||||
| -rw-r--r-- | Swiften/Session/BasicSessionStream.h | 2 | ||||
| -rw-r--r-- | Swiften/Session/SessionStream.h | 5 | ||||
| -rw-r--r-- | Swiften/TLS/CertificateVerificationError.h | 9 | ||||
| -rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLContext.cpp | 65 | ||||
| -rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLContext.h | 2 | ||||
| -rw-r--r-- | Swiften/TLS/SConscript | 1 | 
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) : | 
 Swift
 Swift