diff options
20 files changed, 1091 insertions, 18 deletions
diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct index 4ead554..3fd093b 100644 --- a/BuildTools/SCons/SConstruct +++ b/BuildTools/SCons/SConstruct @@ -503,18 +503,24 @@ if env.get("try_avahi", True) and conf.CheckCHeader("avahi-client/client.h") and  conf.Finish()  # Qt  if env["qt"] :  	env["QTDIR"] = env["qt"] +# Check for OS X Secure Transport +if not env.get("openssl_force_bundled", False) and env["PLATFORM"] == "darwin" and env["target"] == "native"  : +	env["HAVE_SECURETRANSPORT"] = True +else : +	env["HAVE_SECURETRANSPORT"] = False +  # OpenSSL  openssl_env = conf_env.Clone()  if env.get("openssl_force_bundled", False) or env["target"] in ("iphone-device", "iphone-simulator", "xcode", "android") :  	env["OPENSSL_BUNDLED"] = True  	env["HAVE_OPENSSL"] = True -else : +elif not env["HAVE_SECURETRANSPORT"] :  	use_openssl = bool(env["openssl"])  	openssl_prefix = ""  	if isinstance(env["openssl"], str) :  		openssl_prefix = env["openssl"]  	openssl_flags = {}  	if openssl_prefix : @@ -713,9 +719,9 @@ if env.get("HAVE_EXPAT", 0):  	if env.get("EXPAT_BUNDLED", False) :  		parsers.append("(Bundled)")  print "  Projects: " + ' '.join(env["PROJECTS"])  print ""  print "  XML Parsers: " + ' '.join(parsers) -print "  TLS Support: " + (env.get("HAVE_OPENSSL",0) and "OpenSSL" or env.get("HAVE_SCHANNEL", 0) and "Schannel" or "Disabled") +print "  TLS Support: " + (env.get("HAVE_OPENSSL",0) and "OpenSSL" or env.get("HAVE_SECURETRANSPORT",0) and "Secure Transport" or env.get("HAVE_SCHANNEL", 0) and "Schannel" or "Disabled")  print "  DNSSD Support: " + (env.get("HAVE_BONJOUR") and "Bonjour" or (env.get("HAVE_AVAHI") and "Avahi" or "Disabled"))  print diff --git a/Swift/SConscript b/Swift/SConscript index 31b0b94..bf19873 100644 --- a/Swift/SConscript +++ b/Swift/SConscript @@ -2,13 +2,13 @@ import datetime  Import("env")  SConscript("Controllers/SConscript")  if env["SCONS_STAGE"] == "build" : -	if not GetOption("help") and not env.get("HAVE_OPENSSL", 0) and not env.get("HAVE_SCHANNEL", 0) : +	if not GetOption("help") and not env.get("HAVE_OPENSSL", 0) and not env.get("HAVE_SCHANNEL", 0) and not env.get("HAVE_SECURETRANSPORT", 0):  		print "Error: Swift requires OpenSSL support, and OpenSSL was not found."  		if "Swift" in env["PROJECTS"] :  			env["PROJECTS"].remove("Swift")  	elif not GetOption("help") and not env.get("HAVE_QT", 0) :  		if "Swift" in env["PROJECTS"] :  			print "Warning: Swift requires Qt. Not building the Swift Qt application." diff --git a/Swiften/Crypto/UnitTest/CryptoProviderTest.cpp b/Swiften/Crypto/UnitTest/CryptoProviderTest.cpp index 575c75e..a53a0cf 100644 --- a/Swiften/Crypto/UnitTest/CryptoProviderTest.cpp +++ b/Swiften/Crypto/UnitTest/CryptoProviderTest.cpp @@ -1,8 +1,8 @@  /*
 - * Copyright (c) 2010-2013 Isode Limited.
 + * Copyright (c) 2010-2015 Isode Limited.
   * All rights reserved.
   * See the COPYING file for more information.
   */
  #include <Swiften/Base/ByteArray.h>
  #include <Swiften/Base/Platform.h>
 @@ -14,13 +14,13 @@  #ifdef SWIFTEN_PLATFORM_WIN32
  #include <Swiften/Crypto/WindowsCryptoProvider.h>
  #endif
  #ifdef HAVE_OPENSSL_CRYPTO_PROVIDER
  #include <Swiften/Crypto/OpenSSLCryptoProvider.h>
  #endif
 -#ifdef HAVE_OPENSSL_CRYPTO_PROVIDER
 +#ifdef HAVE_COMMONCRYPTO_CRYPTO_PROVIDER
  #include <Swiften/Crypto/CommonCryptoCryptoProvider.h>
  #endif
  #include <Swiften/Crypto/Hash.h>
  using namespace Swift;
 diff --git a/Swiften/Network/TLSConnection.cpp b/Swiften/Network/TLSConnection.cpp index 149548a..c69547d 100644 --- a/Swiften/Network/TLSConnection.cpp +++ b/Swiften/Network/TLSConnection.cpp @@ -1,8 +1,8 @@  /* - * Copyright (c) 2011 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #include <Swiften/Network/TLSConnection.h> @@ -63,12 +63,16 @@ void TLSConnection::write(const SafeByteArray& data) {  }  HostAddressPort TLSConnection::getLocalAddress() const {  	return connection->getLocalAddress();  } +TLSContext* TLSConnection::getTLSContext() const { +	return context; +} +  void TLSConnection::handleRawConnectFinished(bool error) {  	connection->onConnectFinished.disconnect(boost::bind(&TLSConnection::handleRawConnectFinished, this, _1));  	if (error) {  		onConnectFinished(true);  	}  	else { diff --git a/Swiften/Network/TLSConnection.h b/Swiften/Network/TLSConnection.h index 96525ad..a037eb1 100644 --- a/Swiften/Network/TLSConnection.h +++ b/Swiften/Network/TLSConnection.h @@ -3,22 +3,21 @@   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once -#include <boost/shared_ptr.hpp>  #include <boost/enable_shared_from_this.hpp> -#include <Swiften/Base/boost_bsignals.h> +#include <boost/shared_ptr.hpp>  #include <Swiften/Base/API.h>  #include <Swiften/Base/SafeByteArray.h> +#include <Swiften/Base/boost_bsignals.h>  #include <Swiften/Network/Connection.h>  #include <Swiften/TLS/TLSOptions.h> -  namespace Swift {  	class HostAddressPort;  	class TLSContextFactory;  	class TLSContext;  	class SWIFTEN_API TLSConnection : public Connection { @@ -31,19 +30,22 @@ namespace Swift {  			virtual void connect(const HostAddressPort& address);  			virtual void disconnect();  			virtual void write(const SafeByteArray& data);  			virtual HostAddressPort getLocalAddress() const; +			TLSContext* getTLSContext() const; +  		private:  			void handleRawConnectFinished(bool error);  			void handleRawDisconnected(const boost::optional<Error>& error);  			void handleRawDataRead(boost::shared_ptr<SafeByteArray> data);  			void handleRawDataWritten();  			void handleTLSConnectFinished(bool error);  			void handleTLSDataForNetwork(const SafeByteArray& data);  			void handleTLSDataForApplication(const SafeByteArray& data); +  		private:  			TLSContext* context;  			Connection::ref connection;  	};  } diff --git a/Swiften/QA/TLSTest/CertificateErrorTest.cpp b/Swiften/QA/TLSTest/CertificateErrorTest.cpp new file mode 100644 index 0000000..d7c2c55 --- /dev/null +++ b/Swiften/QA/TLSTest/CertificateErrorTest.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + + /* +	This file uses http://www.tls-o-matic.com/ to test the currently configured TLS backend for correct certificate validation behavior. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Swiften/Base/Log.h> +#include <Swiften/EventLoop/DummyEventLoop.h> +#include <Swiften/IDN/PlatformIDNConverter.h> +#include <Swiften/IDN/IDNConverter.h> +#include <Swiften/Network/BoostConnectionFactory.h> +#include <Swiften/Network/BoostIOServiceThread.h> +#include <Swiften/Network/HostAddressPort.h> +#include <Swiften/Network/PlatformDomainNameResolver.h> +#include <Swiften/Network/TLSConnection.h> +#include <Swiften/Network/TLSConnectionFactory.h> +#include <Swiften/TLS/CertificateVerificationError.h> +#include <Swiften/TLS/PlatformTLSFactories.h> +#include <Swiften/TLS/TLSContext.h> +#include <Swiften/TLS/TLSContextFactory.h> + +using namespace Swift; + +class CertificateErrorTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(CertificateErrorTest); + +		// These test require the TLS-O-Matic testing CA to be trusted. For more info see https://www.tls-o-matic.com/https/test1 . +		CPPUNIT_TEST(testTLS_O_MaticTrusted); +		CPPUNIT_TEST(testTLS_O_MaticCertificateFromTheFuture); +		CPPUNIT_TEST(testTLS_O_MaticCertificateFromThePast); +		CPPUNIT_TEST(testTLS_O_MaticCertificateFromUnknownCA); +		CPPUNIT_TEST(testTLS_O_MaticCertificateWrongPurpose); + +#if !defined(HAVE_OPENSSL) +		// Our OpenSSL backend does not support revocation. We excluded it from the revocation tests. +		CPPUNIT_TEST(testRevokedCertificateRevocationDisabled); +		CPPUNIT_TEST(testRevokedCertificateRevocationEnabled); +#endif + +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void setUp() { +			SWIFT_LOG(debug) << std::endl << std::endl; +			eventLoop_ = new DummyEventLoop(); +			boostIOServiceThread_ = new BoostIOServiceThread(); +			boostIOService_ = boost::make_shared<boost::asio::io_service>(); +			connectionFactory_ = new BoostConnectionFactory(boostIOServiceThread_->getIOService(), eventLoop_); +			idnConverter_ = PlatformIDNConverter::create(); +			domainNameResolver_ = new PlatformDomainNameResolver(idnConverter_, eventLoop_), + +			tlsFactories_ = new PlatformTLSFactories(); +			tlsContextFactory_ = tlsFactories_->getTLSContextFactory(); +			 +			tlsContextFactory_->setCheckCertificateRevocation(false); + +			tlsConnectionFactory_ = new TLSConnectionFactory(tlsContextFactory_, connectionFactory_, TLSOptions()); + +			connectFinished_ = false; +			connectFinishedWithError_ = false; +		} + +		void tearDown() { +			delete tlsConnectionFactory_; +			delete tlsFactories_; + +			delete domainNameResolver_; +			delete idnConverter_; +			delete connectionFactory_; +			delete boostIOServiceThread_; +			while (eventLoop_->hasEvents()) { +				eventLoop_->processEvents(); +			} +			delete eventLoop_; +		} + +		HostAddress resolveName(const std::string& name) { +			boost::shared_ptr<DomainNameAddressQuery> query = domainNameResolver_->createAddressQuery(name); +			query->onResult.connect(boost::bind(&CertificateErrorTest::handleAddressQueryResult, this, _1, _2)); +			lastResoverResult_ = HostAddress(); +			resolvingDone_ = false; +			 +			query->run(); +			while(!resolvingDone_) { +				eventLoop_->processEvents(); +			} + +			return lastResoverResult_; +		} + +		void connectToServer(boost::shared_ptr<TLSConnection> connection, const std::string& hostname, int port) { +			Log::setLogLevel(Log::debug); +			connection->onConnectFinished.connect(boost::bind(&CertificateErrorTest::handleConnectFinished, this, _1)); + +			HostAddress address = resolveName(hostname); + +			connection->connect(HostAddressPort(address, port)); + +			while (!connectFinished_) { +				eventLoop_->processEvents(); +			} +		} + +		void testTLS_O_MaticTrusted() { +			boost::shared_ptr<TLSConnection> connection = boost::dynamic_pointer_cast<TLSConnection>(tlsConnectionFactory_->createConnection()); +			TLSContext* context = connection->getTLSContext(); + +			connectToServer(connection, "test1.tls-o-matic.com", 443); +	 +			CPPUNIT_ASSERT_EQUAL(false, connectFinishedWithError_); +			CPPUNIT_ASSERT_EQUAL(CertificateVerificationError::ref(), context->getPeerCertificateVerificationError()); +		} + +		void testTLS_O_MaticCertificateFromTheFuture() { +			boost::shared_ptr<TLSConnection> connection = boost::dynamic_pointer_cast<TLSConnection>(tlsConnectionFactory_->createConnection()); +			TLSContext* context = connection->getTLSContext(); + +			connectToServer(connection, "test5.tls-o-matic.com", 405); + +			CPPUNIT_ASSERT_EQUAL(true, connectFinishedWithError_); +			CPPUNIT_ASSERT(context->getPeerCertificateVerificationError()); +			CPPUNIT_ASSERT_EQUAL(CertificateVerificationError::NotYetValid, context->getPeerCertificateVerificationError()->getType()); +		} + +		void testTLS_O_MaticCertificateFromThePast() { +			boost::shared_ptr<TLSConnection> connection = boost::dynamic_pointer_cast<TLSConnection>(tlsConnectionFactory_->createConnection()); +			TLSContext* context = connection->getTLSContext(); + +			connectToServer(connection, "test6.tls-o-matic.com", 406); + +			CPPUNIT_ASSERT_EQUAL(true, connectFinishedWithError_); +			CPPUNIT_ASSERT(context->getPeerCertificateVerificationError()); +			CPPUNIT_ASSERT_EQUAL(CertificateVerificationError::Expired, context->getPeerCertificateVerificationError()->getType()); +		} + +		void testTLS_O_MaticCertificateFromUnknownCA() { +			Log::setLogLevel(Log::debug); +			boost::shared_ptr<TLSConnection> connection = boost::dynamic_pointer_cast<TLSConnection>(tlsConnectionFactory_->createConnection()); +			TLSContext* context = connection->getTLSContext(); + +			connectToServer(connection, "test7.tls-o-matic.com", 407); + +			CPPUNIT_ASSERT_EQUAL(true, connectFinishedWithError_); +			CPPUNIT_ASSERT(context->getPeerCertificateVerificationError()); +			CPPUNIT_ASSERT_EQUAL(CertificateVerificationError::Untrusted, context->getPeerCertificateVerificationError()->getType()); +		} + +		// test14.tls-o-matic.com:414 +		void testTLS_O_MaticCertificateWrongPurpose() { +			boost::shared_ptr<TLSConnection> connection = boost::dynamic_pointer_cast<TLSConnection>(tlsConnectionFactory_->createConnection()); +			TLSContext* context = connection->getTLSContext(); + +			connectToServer(connection, "test14.tls-o-matic.com", 414); + +			CPPUNIT_ASSERT_EQUAL(true, connectFinishedWithError_); +			CPPUNIT_ASSERT(context->getPeerCertificateVerificationError()); +			CPPUNIT_ASSERT_EQUAL(CertificateVerificationError::InvalidPurpose, context->getPeerCertificateVerificationError()->getType()); +		} + +		void testRevokedCertificateRevocationDisabled() { +			tlsContextFactory_->setCheckCertificateRevocation(false); +			boost::shared_ptr<TLSConnection> connection = boost::dynamic_pointer_cast<TLSConnection>(tlsConnectionFactory_->createConnection()); +			TLSContext* context = connection->getTLSContext(); + +			connectToServer(connection, "revoked.grc.com", 443); + +			CPPUNIT_ASSERT_EQUAL(false, connectFinishedWithError_); +			CPPUNIT_ASSERT(!context->getPeerCertificateVerificationError()); +		} + +		void testRevokedCertificateRevocationEnabled() { +			tlsContextFactory_->setCheckCertificateRevocation(true); +			boost::shared_ptr<TLSConnection> connection = boost::dynamic_pointer_cast<TLSConnection>(tlsConnectionFactory_->createConnection()); +			TLSContext* context = connection->getTLSContext(); + +			connectToServer(connection, "revoked.grc.com", 443); + +			CPPUNIT_ASSERT_EQUAL(true, connectFinishedWithError_); +			CPPUNIT_ASSERT(context->getPeerCertificateVerificationError()); +			CPPUNIT_ASSERT_EQUAL(CertificateVerificationError::Revoked, context->getPeerCertificateVerificationError()->getType()); +		} + +	private: +		void handleAddressQueryResult(const std::vector<HostAddress>& address, boost::optional<DomainNameResolveError> /* error */) { +			if (address.size() > 0) { +				lastResoverResult_ = address[0]; +			} +			resolvingDone_ = true; +		} + +		void handleConnectFinished(bool error) { +			connectFinished_ = true; +			connectFinishedWithError_ = error; +		} +	 +	private: +		BoostIOServiceThread* boostIOServiceThread_; +		boost::shared_ptr<boost::asio::io_service> boostIOService_; +		DummyEventLoop* eventLoop_; +		ConnectionFactory* connectionFactory_; +		PlatformTLSFactories* tlsFactories_; +		TLSContextFactory* tlsContextFactory_; +		TLSConnectionFactory* tlsConnectionFactory_; +		 +		IDNConverter* idnConverter_; +		DomainNameResolver* domainNameResolver_; +		HostAddress lastResoverResult_; +		bool resolvingDone_; + +		bool connectFinished_; +		bool connectFinishedWithError_; +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(CertificateErrorTest); diff --git a/Swiften/QA/TLSTest/SConscript b/Swiften/QA/TLSTest/SConscript index 18f6998..c597ab1 100644 --- a/Swiften/QA/TLSTest/SConscript +++ b/Swiften/QA/TLSTest/SConscript @@ -9,8 +9,9 @@ if env["TEST"] :  	myenv.MergeFlags(myenv["SWIFTEN_FLAGS"])  	myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"])  	myenv.MergeFlags(myenv["CPPUNIT_FLAGS"])  	tester = myenv.Program("TLSTest", [  			"CertificateTest.cpp", +			"CertificateErrorTest.cpp"  		])  	myenv.Test(tester, "system") diff --git a/Swiften/SConscript b/Swiften/SConscript index 9216b39..9b82434 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -560,13 +560,13 @@ if env["SCONS_STAGE"] == "build" :  			# Library-specfifc private modules  			if root.endswith("OpenSSL") or root.endswith("Cocoa") or root.endswith("Qt") or root.endswith("Avahi") or root.endswith("Bonjour") :  				continue  			# Library-specific files -			if file.endswith("_Private.h") or file.startswith("Schannel") or file.startswith("CAPI") or file.startswith("MacOSX") or file.startswith("Windows") or file.endswith("_Windows.h") or file.startswith("SQLite") or file == "ICUConverter.h" or file == "UnboundDomainNameResolver.h" : +			if file.endswith("_Private.h") or file.startswith("Schannel") or file.startswith("CAPI") or file.startswith("MacOSX") or file.startswith("SecureTransport") or file.startswith("Windows") or file.endswith("_Windows.h") or file.startswith("SQLite") or file == "ICUConverter.h" or file == "UnboundDomainNameResolver.h" :  				continue  			# Specific headers we don't want to globally include  			if file == "Swiften.h" or file == "foreach.h" or file == "Log.h" or file == "format.h" :  				continue  			swiften_header += "#include <" + include + ">\n" diff --git a/Swiften/TLS/Certificate.h b/Swiften/TLS/Certificate.h index a39126c..00d618e 100644 --- a/Swiften/TLS/Certificate.h +++ b/Swiften/TLS/Certificate.h @@ -1,17 +1,19 @@  /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once +#include <string> +#include <vector> +  #include <boost/shared_ptr.hpp> -#include <string>  #include <Swiften/Base/API.h>  #include <Swiften/Base/ByteArray.h>  namespace Swift {  	class CryptoProvider; diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h index 73e4322..73fe75c 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.h +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h @@ -1,28 +1,29 @@  /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once -#include <openssl/ssl.h> -#include <Swiften/Base/boost_bsignals.h>  #include <boost/noncopyable.hpp> -#include <Swiften/TLS/TLSContext.h> +#include <openssl/ssl.h> +  #include <Swiften/Base/ByteArray.h> +#include <Swiften/Base/boost_bsignals.h>  #include <Swiften/TLS/CertificateWithKey.h> +#include <Swiften/TLS/TLSContext.h>  namespace Swift {  	class OpenSSLContext : public TLSContext, boost::noncopyable {  		public:  			OpenSSLContext(); -			~OpenSSLContext(); +			virtual ~OpenSSLContext();  			void connect();  			bool setClientCertificate(CertificateWithKey::ref cert);  			void handleDataFromNetwork(const SafeByteArray&);  			void handleDataFromApplication(const SafeByteArray&); diff --git a/Swiften/TLS/PlatformTLSFactories.cpp b/Swiften/TLS/PlatformTLSFactories.cpp index 2492840..588e0e1 100644 --- a/Swiften/TLS/PlatformTLSFactories.cpp +++ b/Swiften/TLS/PlatformTLSFactories.cpp @@ -15,24 +15,32 @@  	#include <Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h>  #endif  #ifdef HAVE_SCHANNEL  	#include <Swiften/TLS/Schannel/SchannelContextFactory.h>  	#include <Swiften/TLS/Schannel/SchannelCertificateFactory.h>  #endif +#ifdef HAVE_SECURETRANSPORT +	#include <Swiften/TLS/SecureTransport/SecureTransportContextFactory.h> +	#include <Swiften/TLS/SecureTransport/SecureTransportCertificateFactory.h> +#endif  namespace Swift {  PlatformTLSFactories::PlatformTLSFactories() : contextFactory(NULL), certificateFactory(NULL) {  #ifdef HAVE_OPENSSL  	contextFactory = new OpenSSLContextFactory();  	certificateFactory = new OpenSSLCertificateFactory();  #endif  #ifdef HAVE_SCHANNEL  	contextFactory = new SchannelContextFactory();  	certificateFactory = new SchannelCertificateFactory();  #endif +#ifdef HAVE_SECURETRANSPORT +	contextFactory = new SecureTransportContextFactory(); +	certificateFactory = new SecureTransportCertificateFactory(); +#endif	  }  PlatformTLSFactories::~PlatformTLSFactories() {  	delete contextFactory;  	delete certificateFactory;  } diff --git a/Swiften/TLS/SConscript b/Swiften/TLS/SConscript index fb327b9..f5eb053 100644 --- a/Swiften/TLS/SConscript +++ b/Swiften/TLS/SConscript @@ -24,12 +24,20 @@ elif myenv.get("HAVE_SCHANNEL", 0) :  			"CAPICertificate.cpp",  			"Schannel/SchannelContext.cpp",  			"Schannel/SchannelCertificate.cpp",  			"Schannel/SchannelContextFactory.cpp",  		])  	myenv.Append(CPPDEFINES = "HAVE_SCHANNEL") +elif myenv.get("HAVE_SECURETRANSPORT", 0) : +	#swiften_env.Append(LIBS = ["Winscard"]) +	objects += myenv.StaticObject([ +			"SecureTransport/SecureTransportContext.mm", +			"SecureTransport/SecureTransportCertificate.mm", +			"SecureTransport/SecureTransportContextFactory.cpp", +		]) +	myenv.Append(CPPDEFINES = "HAVE_SECURETRANSPORT")  objects += myenv.SwiftenObject(["PlatformTLSFactories.cpp"])  swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/TLS/Schannel/SchannelContext.h b/Swiften/TLS/Schannel/SchannelContext.h index 36a3f0c..be30a7c 100644 --- a/Swiften/TLS/Schannel/SchannelContext.h +++ b/Swiften/TLS/Schannel/SchannelContext.h @@ -36,13 +36,13 @@ namespace Swift  	public:  		typedef boost::shared_ptr<SchannelContext> sp_t;  	public:  		SchannelContext(bool tls1_0Workaround); -		~SchannelContext(); +		virtual ~SchannelContext();  		//  		// TLSContext  		//  		virtual void	connect();  		virtual bool	setClientCertificate(CertificateWithKey::ref cert); diff --git a/Swiften/TLS/SecureTransport/SecureTransportCertificate.h b/Swiften/TLS/SecureTransport/SecureTransportCertificate.h new file mode 100644 index 0000000..b8d3728 --- /dev/null +++ b/Swiften/TLS/SecureTransport/SecureTransportCertificate.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/type_traits.hpp> + +#include <Security/SecCertificate.h> + +#include <Swiften/TLS/Certificate.h> + +namespace Swift { + +class SecureTransportCertificate : public Certificate { +public: +	SecureTransportCertificate(SecCertificateRef certificate); +	SecureTransportCertificate(const ByteArray& der); +	virtual ~SecureTransportCertificate(); + +	virtual std::string getSubjectName() const; +	virtual std::vector<std::string> getCommonNames() const; +	virtual std::vector<std::string> getSRVNames() const; +	virtual std::vector<std::string> getDNSNames() const; +	virtual std::vector<std::string> getXMPPAddresses() const; + +	virtual ByteArray toDER() const; + +private: +	void parse(); +	typedef boost::remove_pointer<SecCertificateRef>::type SecCertificate; + +private: +	boost::shared_ptr<SecCertificate> certificateHandle_; +	std::string subjectName_; +	std::vector<std::string> commonNames_; +	std::vector<std::string> srvNames_; +	std::vector<std::string> dnsNames_; +	std::vector<std::string> xmppAddresses_; +}; + +} diff --git a/Swiften/TLS/SecureTransport/SecureTransportCertificate.mm b/Swiften/TLS/SecureTransport/SecureTransportCertificate.mm new file mode 100644 index 0000000..3b4e00f --- /dev/null +++ b/Swiften/TLS/SecureTransport/SecureTransportCertificate.mm @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swiften/TLS/SecureTransport/SecureTransportCertificate.h> + +#include <boost/numeric/conversion/cast.hpp> + +#include <Cocoa/Cocoa.h> +#include <Security/Security.h> + +#include <Swiften/Base/Log.h> + +namespace { + +template <typename T, typename S> +T bridge_cast(S source) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +	return (__bridge T)(source); +#pragma clang diagnostic pop +} + +} + +namespace Swift { + +SecureTransportCertificate::SecureTransportCertificate(SecCertificateRef certificate) { +	assert(certificate); +	CFRetain(certificate); +	certificateHandle_ = boost::shared_ptr<SecCertificate>(certificate, CFRelease); +	parse(); +} + + +SecureTransportCertificate::SecureTransportCertificate(const ByteArray& der) { +	CFDataRef derData = CFDataCreateWithBytesNoCopy(NULL, der.data(), static_cast<CFIndex>(der.size()), NULL);  +	SecCertificateRef certificate = SecCertificateCreateWithData(NULL, derData); +	if (certificate) { +		certificateHandle_ = boost::shared_ptr<SecCertificate>(certificate, CFRelease); +		parse(); +	} +} + +SecureTransportCertificate::~SecureTransportCertificate() { + +} + +#define NS2STDSTRING(a) (a == nil ? std::string() : std::string([a cStringUsingEncoding:NSUTF8StringEncoding])) + + +void SecureTransportCertificate::parse() { +	assert(certificateHandle_); +	CFErrorRef error = NULL; + +	// The SecCertificateCopyValues function is not part of the iOS Secure Transport API. +	CFDictionaryRef valueDict = SecCertificateCopyValues(certificateHandle_.get(), 0, &error); +	if (error) { +		CFRelease(error); +	} +	else { +		// Handle subject. +		CFStringRef subject = SecCertificateCopySubjectSummary(certificateHandle_.get()); +		if (subject) { +			NSString* subjectStr = bridge_cast<NSString*>(subject); +			subjectName_ = NS2STDSTRING(subjectStr); +			CFRelease(subject); +		} + +		// Handle a single Common Name. +		CFStringRef commonName; +		OSStatus error = SecCertificateCopyCommonName(certificateHandle_.get(), &commonName); +		if (!error) { +			NSString* commonNameStr = bridge_cast<NSString*>(commonName); +			commonNames_.push_back(NS2STDSTRING(commonNameStr)); +			CFRelease(commonName); +		} + +		// Handle Subject Alternative Names +		NSDictionary* certDict = bridge_cast<NSDictionary*>(valueDict); +		NSDictionary* subjectAltNamesDict = certDict[@"2.5.29.17"][@"value"]; + +		for (NSDictionary* entry in subjectAltNamesDict) { +			if ([entry[@"label"] isEqualToString:[NSString stringWithUTF8String:ID_ON_XMPPADDR_OID]]) { +				xmppAddresses_.push_back(NS2STDSTRING(entry[@"value"])); +			} +			else if ([entry[@"label"] isEqualToString:[NSString stringWithUTF8String:ID_ON_DNSSRV_OID]]) { +				srvNames_.push_back(NS2STDSTRING(entry[@"value"])); +			} +			else if ([entry[@"label"] isEqualToString:@"DNS Name"]) { +				dnsNames_.push_back(NS2STDSTRING(entry[@"value"])); +			} +		} +		CFRelease(valueDict); +	} +} + +std::string SecureTransportCertificate::getSubjectName() const { +	return subjectName_; +} + +std::vector<std::string> SecureTransportCertificate::getCommonNames() const { +	return commonNames_; +} + +std::vector<std::string> SecureTransportCertificate::getSRVNames() const { +	return srvNames_; +} + +std::vector<std::string> SecureTransportCertificate::getDNSNames() const { +	return dnsNames_; +} + +std::vector<std::string> SecureTransportCertificate::getXMPPAddresses() const { +	return xmppAddresses_; +} + +ByteArray SecureTransportCertificate::toDER() const { +	ByteArray der; +	if (certificateHandle_) { +		CFDataRef derData = SecCertificateCopyData(certificateHandle_.get()); +		if (derData) { +			try { +				size_t dataSize = boost::numeric_cast<size_t>(CFDataGetLength(derData)); +				der.resize(dataSize); +				CFDataGetBytes(derData, CFRangeMake(0,CFDataGetLength(derData)), der.data()); +			} catch (...) { +			} +			CFRelease(derData); +		} +	} +	return der; +} + +} diff --git a/Swiften/TLS/SecureTransport/SecureTransportCertificateFactory.h b/Swiften/TLS/SecureTransport/SecureTransportCertificateFactory.h new file mode 100644 index 0000000..1f86541 --- /dev/null +++ b/Swiften/TLS/SecureTransport/SecureTransportCertificateFactory.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/TLS/CertificateFactory.h> +#include <Swiften/TLS/SecureTransport/SecureTransportCertificate.h> + +namespace Swift { +	 +class SecureTransportCertificateFactory : public CertificateFactory { +	public: +		virtual Certificate* createCertificateFromDER(const ByteArray& der) { +			return new SecureTransportCertificate(der); +		} +	}; +} diff --git a/Swiften/TLS/SecureTransport/SecureTransportContext.h b/Swiften/TLS/SecureTransport/SecureTransportContext.h new file mode 100644 index 0000000..aa17c66 --- /dev/null +++ b/Swiften/TLS/SecureTransport/SecureTransportContext.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Security/SecureTransport.h> + +#include <Swiften/TLS/TLSContext.h> + +namespace Swift { + +class SecureTransportContext : public TLSContext { +	public: +		SecureTransportContext(bool checkCertificateRevocation); +		virtual ~SecureTransportContext(); + +		virtual void connect(); + +		virtual bool setClientCertificate(CertificateWithKey::ref cert); + +		virtual void handleDataFromNetwork(const SafeByteArray&); +		virtual void handleDataFromApplication(const SafeByteArray&); + +		virtual std::vector<Certificate::ref> getPeerCertificateChain() const; +		virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const; + +		virtual ByteArray getFinishMessage() const; +	 +	private: +		static OSStatus SSLSocketReadCallback(SSLConnectionRef connection, void *data, size_t *dataLength);  +		static OSStatus SSLSocketWriteCallback(SSLConnectionRef connection, const void *data, size_t *dataLength); + +	private: +		enum State { None, Handshake, HandshakeDone, Error}; +		static std::string stateToString(State state); +		void setState(State newState); + +		static boost::shared_ptr<TLSError> nativeToTLSError(OSStatus error); +		boost::shared_ptr<CertificateVerificationError> CSSMErrorToVerificationError(OSStatus resultCode); + +		void processHandshake(); +		void verifyServerCertificate(); + +		void fatalError(boost::shared_ptr<TLSError> error, boost::shared_ptr<CertificateVerificationError> certificateError); + +	private: +		boost::shared_ptr<SSLContext> sslContext_; +		SafeByteArray readingBuffer_; +		State state_; +		CertificateVerificationError::ref verificationError_; +		CertificateWithKey::ref clientCertificate_; +		bool checkCertificateRevocation_; +}; + +} diff --git a/Swiften/TLS/SecureTransport/SecureTransportContext.mm b/Swiften/TLS/SecureTransport/SecureTransportContext.mm new file mode 100644 index 0000000..7f44f7d --- /dev/null +++ b/Swiften/TLS/SecureTransport/SecureTransportContext.mm @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swiften/TLS/SecureTransport/SecureTransportContext.h> + +#include <boost/type_traits.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Swiften/Base/Algorithm.h> +#include <Swiften/Base/Log.h> +#include <Swiften/TLS/SecureTransport/SecureTransportCertificate.h> +#include <Swiften/TLS/PKCS12Certificate.h> +#include <Swiften/TLS/CertificateWithKey.h> + +#include <Cocoa/Cocoa.h> + +#import <Security/SecCertificate.h> +#import <Security/SecImportExport.h> + +namespace { +	typedef boost::remove_pointer<CFArrayRef>::type CFArray; +	typedef boost::remove_pointer<SecTrustRef>::type SecTrust; +} + +template <typename T, typename S> +T bridge_cast(S source) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +	return (__bridge T)(source); +#pragma clang diagnostic pop +} + +namespace Swift { + +namespace { + +	 +CFArrayRef getClientCertificateChainAsCFArrayRef(CertificateWithKey::ref key) { +	boost::shared_ptr<PKCS12Certificate> pkcs12 = boost::dynamic_pointer_cast<PKCS12Certificate>(key); +	if (!key) { +		return NULL; +	} + +	SafeByteArray safePassword = pkcs12->getPassword(); +	CFIndex passwordSize = 0; +	try { +		passwordSize = boost::numeric_cast<CFIndex>(safePassword.size()); +	} catch (...) { +		return NULL; +	} + +	CFMutableArrayRef certChain = CFArrayCreateMutable(NULL, 0, 0); + +	OSStatus securityError = errSecSuccess; +	CFStringRef password = CFStringCreateWithBytes(kCFAllocatorDefault, safePassword.data(), passwordSize, kCFStringEncodingUTF8, false); +	const void* keys[] = { kSecImportExportPassphrase }; +	const void* values[] = { password }; + +	CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); + +	CFArrayRef items = NULL; +	CFDataRef pkcs12Data = bridge_cast<CFDataRef>([NSData dataWithBytes: static_cast<const void *>(pkcs12->getData().data()) length:pkcs12->getData().size()]); +	securityError = SecPKCS12Import(pkcs12Data, options, &items); +	NSArray* nsItems = bridge_cast<NSArray*>(items); + +	switch(securityError) { +		case errSecSuccess: +			break; +		case errSecAuthFailed: +			// Password did not work for decoding the certificate. +		case errSecDecode: +			// Other decoding error. +		default: +			CFRelease(certChain); +			CFRelease(items); +			CFRelease(options); +			certChain = NULL; +	} + +	if (certChain) { +		CFArrayAppendValue(certChain, nsItems[0][@"identity"]); + +		for (CFIndex index = 0; index < CFArrayGetCount(bridge_cast<CFArrayRef>(nsItems[0][@"chain"])); index++) { +			CFArrayAppendValue(certChain, CFArrayGetValueAtIndex(bridge_cast<CFArrayRef>(nsItems[0][@"chain"]), index)); +		} +	} +	return certChain; +} + +} + +SecureTransportContext::SecureTransportContext(bool checkCertificateRevocation) : state_(None), checkCertificateRevocation_(checkCertificateRevocation) { +	sslContext_ = boost::shared_ptr<SSLContext>(SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType), CFRelease); + +	OSStatus error = noErr; +	// set IO callbacks +	error = SSLSetIOFuncs(sslContext_.get(), &SecureTransportContext::SSLSocketReadCallback, &SecureTransportContext::SSLSocketWriteCallback); +	if (error != noErr) { +		SWIFT_LOG(error) << "Unable to set IO functions to SSL context." << std::endl; +		sslContext_.reset(); +	} + +	error = SSLSetConnection(sslContext_.get(), this); +	if (error != noErr) { +		SWIFT_LOG(error) << "Unable to set connection to SSL context." << std::endl; +		sslContext_.reset(); +	} + + +	error = SSLSetSessionOption(sslContext_.get(), kSSLSessionOptionBreakOnServerAuth, true); +	if (error != noErr) { +		SWIFT_LOG(error) << "Unable to set kSSLSessionOptionBreakOnServerAuth on session." << std::endl; +		sslContext_.reset(); +	} +} + +SecureTransportContext::~SecureTransportContext() { +	if (sslContext_) { +		SSLClose(sslContext_.get()); +	} +} + +std::string SecureTransportContext::stateToString(State state) { +	std::string returnValue; +	switch(state) { +		case Handshake: +			returnValue = "Handshake"; +			break; +		case HandshakeDone: +			returnValue = "HandshakeDone"; +			break; +		case None: +			returnValue = "None"; +			break; +		case Error: +			returnValue = "Error"; +			break; +	} +	return returnValue; +} + +void SecureTransportContext::setState(State newState) { +	SWIFT_LOG(debug) << "Switch state from " << stateToString(state_) << " to " << stateToString(newState) << "." << std::endl; +	state_ = newState; +} + +void SecureTransportContext::connect() { +	SWIFT_LOG_ASSERT(state_ == None, error) << "current state '" << stateToString(state_) << " invalid." << std::endl; +	if (clientCertificate_) { +		CFArrayRef certs = getClientCertificateChainAsCFArrayRef(clientCertificate_); +		if (certs) { +			boost::shared_ptr<CFArray> certRefs(certs, CFRelease); +			OSStatus result = SSLSetCertificate(sslContext_.get(), certRefs.get()); +			if (result != noErr) { +				SWIFT_LOG(error) << "SSLSetCertificate failed with error " << result << "." << std::endl; +			} +		} +	} +	processHandshake(); +} + +void SecureTransportContext::processHandshake() { +	SWIFT_LOG_ASSERT(state_ == None || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid." << std::endl; +	OSStatus error = SSLHandshake(sslContext_.get()); +	if (error == errSSLWouldBlock) { +		setState(Handshake); +	} +	else if (error == noErr) { +		SWIFT_LOG(debug) << "TLS handshake successful." << std::endl; +		setState(HandshakeDone); +		onConnected(); +	} +	else if (error == errSSLPeerAuthCompleted) { +		SWIFT_LOG(debug) << "Received server certificate. Start verification." << std::endl; +		setState(Handshake); +		verifyServerCertificate(); +	} +	else { +		SWIFT_LOG(debug) << "Error returned from SSLHandshake call is " << error << "." << std::endl; +		fatalError(nativeToTLSError(error), boost::make_shared<CertificateVerificationError>()); +	} +} + + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +void SecureTransportContext::verifyServerCertificate() { +	SecTrustRef trust = NULL; +	OSStatus error = SSLCopyPeerTrust(sslContext_.get(), &trust); +	if (error != noErr) { +		fatalError(boost::make_shared<TLSError>(), boost::make_shared<CertificateVerificationError>()); +		return; +	} +	boost::shared_ptr<SecTrust> trustRef = boost::shared_ptr<SecTrust>(trust, CFRelease); + +	if (checkCertificateRevocation_) { +		error = SecTrustSetOptions(trust, kSecTrustOptionRequireRevPerCert | kSecTrustOptionFetchIssuerFromNet); +		if (error != noErr) { +			fatalError(boost::make_shared<TLSError>(), boost::make_shared<CertificateVerificationError>()); +			return; +		} +	} + +	SecTrustResultType trustResult; +	error = SecTrustEvaluate(trust, &trustResult); +	if (error != errSecSuccess) { +		fatalError(boost::make_shared<TLSError>(), boost::make_shared<CertificateVerificationError>()); +		return; +	} + +	OSStatus cssmResult = 0; +	switch(trustResult) { +		case kSecTrustResultUnspecified: +			SWIFT_LOG(warning) << "Successful implicit validation. Result unspecified." << std::endl; +			break; +		case kSecTrustResultProceed: +			SWIFT_LOG(warning) << "Validation resulted in explicitly trusted." << std::endl; +			break; +		case kSecTrustResultRecoverableTrustFailure: +			SWIFT_LOG(warning) << "recoverable trust failure" << std::endl; +			error = SecTrustGetCssmResultCode(trust, &cssmResult); +			if (error == errSecSuccess) { +				verificationError_ = CSSMErrorToVerificationError(cssmResult); +				if (cssmResult == CSSMERR_TP_VERIFY_ACTION_FAILED || cssmResult == CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK ) { +					// Find out the reason why the verification failed. +					CFArrayRef certChain; +					CSSM_TP_APPLE_EVIDENCE_INFO* statusChain; +					error = SecTrustGetResult(trustRef.get(), &trustResult, &certChain, &statusChain); +					if (error == errSecSuccess) { +						boost::shared_ptr<CFArray> certChainRef = boost::shared_ptr<CFArray>(certChain, CFRelease); +						for (CFIndex index = 0; index < CFArrayGetCount(certChainRef.get()); index++) { +							for (CFIndex n = 0; n < statusChain[index].NumStatusCodes; n++) { +								// Even though Secure Transport reported CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK on the whole certificate +								// chain, the actual cause can be that a revocation check for a specific cert returned CSSMERR_TP_CERT_REVOKED. +								if (!verificationError_ || verificationError_->getType() == CertificateVerificationError::RevocationCheckFailed) { +									verificationError_ = CSSMErrorToVerificationError(statusChain[index].StatusCodes[n]); +								} +							} +						} +					} +					else { + +					} +				} +			} +			else { +				verificationError_ = boost::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError); +			} +			break; +		case kSecTrustResultOtherError: +			verificationError_ = boost::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError); +			break; +		default: +			SWIFT_LOG(warning) << "Unhandled trust result " << trustResult << "." << std::endl; +			break; +	} +	 +	if (verificationError_) { +		setState(Error); +		SSLClose(sslContext_.get()); +		sslContext_.reset(); +		onError(boost::make_shared<TLSError>()); +	} +	else { +		// proceed with handshake +		processHandshake(); +	} +} + +#pragma clang diagnostic pop + +bool SecureTransportContext::setClientCertificate(CertificateWithKey::ref cert) { +	CFArrayRef nativeClientChain = getClientCertificateChainAsCFArrayRef(cert); +	if (nativeClientChain) { +		clientCertificate_ = cert; +		CFRelease(nativeClientChain); +		return true; +	} +	else { +		return false; +	} +} + +void SecureTransportContext::handleDataFromNetwork(const SafeByteArray& data) { +	SWIFT_LOG(debug) << std::endl; +	SWIFT_LOG_ASSERT(state_ == HandshakeDone || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid." << std::endl; + +	append(readingBuffer_, data); + +	size_t bytesRead = 0; +	OSStatus error = noErr; +	SafeByteArray applicationData; + +	switch(state_) { +		case None: +			assert(false && "Invalid state 'None'."); +			break; +		case Handshake: +			processHandshake(); +			break; +		case HandshakeDone: +			while (error == noErr) { +				applicationData.resize(readingBuffer_.size()); +				error = SSLRead(sslContext_.get(), applicationData.data(), applicationData.size(), &bytesRead); +				if (error == noErr) { +					// Read successful. +				} +				else if (error == errSSLWouldBlock) { +					// Secure Transport does not want more data. +					break; +				} +				else { +					SWIFT_LOG(error) << "SSLRead failed with error " << error << ", read bytes: " << bytesRead << "." << std::endl; +					fatalError(boost::make_shared<TLSError>(), boost::make_shared<CertificateVerificationError>()); +					return; +				} + +				if (bytesRead > 0) { +					applicationData.resize(bytesRead); +					onDataForApplication(applicationData); +				} +				else { +					break;	 +				} +			} +			break; +		case Error: +			SWIFT_LOG(debug) << "Igoring received data in error state." << std::endl; +			break; +	} +} + + +void SecureTransportContext::handleDataFromApplication(const SafeByteArray& data) { +	size_t processedBytes = 0; +	OSStatus error = SSLWrite(sslContext_.get(), data.data(), data.size(), &processedBytes); +	switch(error) { +		case errSSLWouldBlock: +			SWIFT_LOG(warning) << "Unexpected because the write callback does not block." << std::endl; +			return; +		case errSSLClosedGraceful: +		case noErr: +			return; +		default: +			SWIFT_LOG(warning) << "SSLWrite returned error code: " << error << ", processed bytes: " << processedBytes << std::endl; +			fatalError(boost::make_shared<TLSError>(), boost::shared_ptr<CertificateVerificationError>()); +	} +} + +std::vector<Certificate::ref> SecureTransportContext::getPeerCertificateChain() const { +	std::vector<Certificate::ref> peerCertificateChain; + +	if (sslContext_) { +			typedef boost::remove_pointer<SecTrustRef>::type SecTrust; +			boost::shared_ptr<SecTrust> securityTrust; + +			SecTrustRef secTrust = NULL;; +			OSStatus error = SSLCopyPeerTrust(sslContext_.get(), &secTrust); +			if (error == noErr) { +				securityTrust = boost::shared_ptr<SecTrust>(secTrust, CFRelease); + +				CFIndex chainSize = SecTrustGetCertificateCount(securityTrust.get()); +				for (CFIndex n = 0; n < chainSize; n++) { +					SecCertificateRef certificate = SecTrustGetCertificateAtIndex(securityTrust.get(), n); +					if (certificate) { +						peerCertificateChain.push_back(boost::make_shared<SecureTransportCertificate>(certificate)); +					} +				} +			} +			else { +				SWIFT_LOG(warning) << "Failed to obtain peer trust structure; error = " << error << "." << std::endl; +			} +	} + +	return peerCertificateChain; +} + +CertificateVerificationError::ref SecureTransportContext::getPeerCertificateVerificationError() const { +	return verificationError_; +} + +ByteArray SecureTransportContext::getFinishMessage() const { +	SWIFT_LOG(warning) << "Access to TLS handshake finish message is not part of OS X Secure Transport APIs." << std::endl; +	return ByteArray(); +} + +/** + *	This I/O callback simulates an asynchronous read to the read buffer of the context. If it is empty, it returns errSSLWouldBlock; else + *  the data within the buffer is returned. + */ +OSStatus SecureTransportContext::SSLSocketReadCallback(SSLConnectionRef connection, void *data, size_t *dataLength) { +	SecureTransportContext* context = const_cast<SecureTransportContext*>(static_cast<const SecureTransportContext*>(connection)); +	OSStatus retValue = noErr; + +	if (context->readingBuffer_.size() < *dataLength) { +		// Would block because Secure Transport is trying to read more data than there currently is available in the buffer. +		*dataLength = 0; +		retValue = errSSLWouldBlock; +	} +	else { +		size_t bufferLen = *dataLength; +		size_t copyToBuffer = bufferLen < context->readingBuffer_.size() ? bufferLen : context->readingBuffer_.size(); + +		memcpy(data, context->readingBuffer_.data(), copyToBuffer); +			 +		context->readingBuffer_ = SafeByteArray(context->readingBuffer_.data() + copyToBuffer, context->readingBuffer_.data() + context->readingBuffer_.size()); +		*dataLength = copyToBuffer; +	} +	return retValue; +} + +OSStatus SecureTransportContext::SSLSocketWriteCallback(SSLConnectionRef connection, const void *data, size_t *dataLength) { +	SecureTransportContext* context = const_cast<SecureTransportContext*>(static_cast<const SecureTransportContext*>(connection)); +	OSStatus retValue = noErr; +	 +	SafeByteArray safeData; +	safeData.resize(*dataLength); +	memcpy(safeData.data(), data, safeData.size()); +	 +	context->onDataForNetwork(safeData); +	return retValue; +} + +boost::shared_ptr<TLSError> SecureTransportContext::nativeToTLSError(OSStatus /* error */) { +	boost::shared_ptr<TLSError> swiftenError; +	swiftenError = boost::make_shared<TLSError>(); +	return swiftenError; +} + +boost::shared_ptr<CertificateVerificationError> SecureTransportContext::CSSMErrorToVerificationError(OSStatus resultCode) { +	boost::shared_ptr<CertificateVerificationError> error; +	switch(resultCode) { +		case CSSMERR_TP_NOT_TRUSTED: +			SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_NOT_TRUSTED" << std::endl; +			error = boost::make_shared<CertificateVerificationError>(CertificateVerificationError::Untrusted); +			break; +		case CSSMERR_TP_CERT_NOT_VALID_YET: +			SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_NOT_VALID_YET" << std::endl; +			error = boost::make_shared<CertificateVerificationError>(CertificateVerificationError::NotYetValid); +			break; +		case CSSMERR_TP_CERT_EXPIRED: +			SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_EXPIRED" << std::endl; +			error = boost::make_shared<CertificateVerificationError>(CertificateVerificationError::Expired); +			break; +		case CSSMERR_TP_CERT_REVOKED: +			SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_REVOKED" << std::endl; +			error = boost::make_shared<CertificateVerificationError>(CertificateVerificationError::Revoked); +			break; +		case CSSMERR_TP_VERIFY_ACTION_FAILED: +			SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_VERIFY_ACTION_FAILED" << std::endl; +			break; +		case CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK: +			SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK" << std::endl; +			if (checkCertificateRevocation_) { +				error = boost::make_shared<CertificateVerificationError>(CertificateVerificationError::RevocationCheckFailed); +			} +			break; +		case CSSMERR_APPLETP_OCSP_UNAVAILABLE: +			SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_OCSP_UNAVAILABLE" << std::endl; +			if (checkCertificateRevocation_) { +				error = boost::make_shared<CertificateVerificationError>(CertificateVerificationError::RevocationCheckFailed); +			} +			break; +		case CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE: +			SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE" << std::endl; +			error = boost::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidPurpose); +			break; +		default: +			SWIFT_LOG(warning) << "unhandled CSSM error: " << resultCode << ", CSSM_TP_BASE_TP_ERROR: " << CSSM_TP_BASE_TP_ERROR << std::endl; +			error = boost::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError); +			break; +	} +	return error; +} + +void SecureTransportContext::fatalError(boost::shared_ptr<TLSError> error, boost::shared_ptr<CertificateVerificationError> certificateError) { +	setState(Error); +	if (sslContext_) { +		SSLClose(sslContext_.get()); +	} +	verificationError_ = certificateError; +	onError(error); +} + +} diff --git a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp new file mode 100644 index 0000000..eb761e9 --- /dev/null +++ b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swiften/TLS/SecureTransport/SecureTransportContextFactory.h> + +#include <Swiften/Base/Log.h> +#include <Swiften/TLS/SecureTransport/SecureTransportContext.h> + +namespace Swift { + +SecureTransportContextFactory::SecureTransportContextFactory() : checkCertificateRevocation_(true), disconnectOnCardRemoval_(true) { + +} + +SecureTransportContextFactory::~SecureTransportContextFactory() { + +} + +bool SecureTransportContextFactory::canCreate() const { +	return true; +} + +TLSContext* SecureTransportContextFactory::createTLSContext(const TLSOptions& /* tlsOptions */) { +	return new SecureTransportContext(checkCertificateRevocation_); +} + +void SecureTransportContextFactory::setCheckCertificateRevocation(bool b) { +	checkCertificateRevocation_ = b; +} + +void SecureTransportContextFactory::setDisconnectOnCardRemoval(bool b) { +	disconnectOnCardRemoval_ = b; +	if (disconnectOnCardRemoval_) { +		SWIFT_LOG(warning) << "Smart cards have not been tested yet" << std::endl; +	} +} + +} diff --git a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h new file mode 100644 index 0000000..f490768 --- /dev/null +++ b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/TLS/TLSContextFactory.h> + +namespace Swift { + +class SecureTransportContextFactory : public TLSContextFactory { +	public: +		SecureTransportContextFactory(); +		virtual ~SecureTransportContextFactory(); + +		virtual bool canCreate() const; + +		virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions); +		virtual void setCheckCertificateRevocation(bool b); +		virtual void setDisconnectOnCardRemoval(bool b); + +	private: +		bool checkCertificateRevocation_; +		bool disconnectOnCardRemoval_; +}; + +}  | 
 Swift