From b2093a372874aefb4f56f66a70a96f78d6cbbaec Mon Sep 17 00:00:00 2001 From: Kevin Smith <git@kismith.co.uk> Date: Wed, 27 Aug 2014 15:16:15 +0100 Subject: Add ability to limit SChannel to TLS 1.0 Some servers have very restrictive TLS stacks that respond badly to a bug in the SChannel TLS implementation, meaning that TLS has to be limited to 1.0. Add ClientOptions.tlsOptions. This is a method of passing options into the TLS stack. It's currently only used for the TLS 1.0 workaround in SChannel, but we might reasonably expose other options in the future, such as limiting cypher suites. Disables use of SSLv3 for SChannel Also updates the coding style in SchannelContext a bit. Test-Information: Compiles on both OS X and Windows(SChannel). OS X doesn't show the new option. Windows shows it, and remembers it between logins. Not tested against a server requiring 1.0 only, but a previous hack with the same approach was tested. Change-Id: I1e7854d43811fd173f21f98d4dc3915fc7a4b322 diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 328d837..c6b6dfc 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -398,7 +398,7 @@ void MainController::handleConnected() { userSearchControllerChat_ = new UserSearchController(UserSearchController::StartChat, jid_, uiEventStream_, client_->getVCardManager(), uiFactory_, client_->getIQRouter(), rosterController_, contactSuggesterWithRoster_, client_->getAvatarManager(), client_->getPresenceOracle()); userSearchControllerAdd_ = new UserSearchController(UserSearchController::AddContact, jid_, uiEventStream_, client_->getVCardManager(), uiFactory_, client_->getIQRouter(), rosterController_, contactSuggesterWithoutRoster_, client_->getAvatarManager(), client_->getPresenceOracle()); adHocManager_ = new AdHocManager(JID(boundJID_.getDomain()), uiFactory_, client_->getIQRouter(), uiEventStream_, rosterController_->getWindow()); - + chatsManager_->onImpromptuMUCServiceDiscovered.connect(boost::bind(&UserSearchController::setCanInitiateImpromptuMUC, userSearchControllerChat_, _1)); } loginWindow_->setIsLoggingIn(false); @@ -410,7 +410,7 @@ void MainController::handleConnected() { discoInfoRequest->send(); client_->getVCardManager()->requestOwnVCard(); - + rosterController_->setJID(boundJID_); rosterController_->setEnabled(true); rosterController_->getWindow()->setStreamEncryptionStatus(client_->isStreamEncrypted()); @@ -841,10 +841,11 @@ std::string MainController::serializeClientOptions(const ClientOptions& options) SERIALIZE_URL(boshHTTPConnectProxyURL); SERIALIZE_SAFE_STRING(boshHTTPConnectProxyAuthID); SERIALIZE_SAFE_STRING(boshHTTPConnectProxyAuthPassword); + SERIALIZE_BOOL(tlsOptions.schannelTLS1_0Workaround); return result; } -#define CHECK_PARSE_LENGTH if (i >= segments.size()) {return result;} +#define CHECK_PARSE_LENGTH if (i >= segments.size()) {return result;} #define PARSE_INT_RAW(defaultValue) CHECK_PARSE_LENGTH intVal = defaultValue; try {intVal = boost::lexical_cast<int>(segments[i]);} catch(const boost::bad_lexical_cast&) {};i++; #define PARSE_STRING_RAW CHECK_PARSE_LENGTH stringVal = byteArrayToString(Base64::decode(segments[i]));i++; @@ -888,6 +889,7 @@ ClientOptions MainController::parseClientOptions(const std::string& optionString PARSE_URL(boshHTTPConnectProxyURL); PARSE_SAFE_STRING(boshHTTPConnectProxyAuthID); PARSE_SAFE_STRING(boshHTTPConnectProxyAuthPassword); + PARSE_BOOL(tlsOptions.schannelTLS1_0Workaround, false); return result; } diff --git a/Swift/QtUI/QtConnectionSettings.ui b/Swift/QtUI/QtConnectionSettings.ui index 2dc46d1..cce60fe 100644 --- a/Swift/QtUI/QtConnectionSettings.ui +++ b/Swift/QtUI/QtConnectionSettings.ui @@ -136,6 +136,13 @@ </widget> </item> <item> + <widget class="QCheckBox" name="manual_forceTLS1_0"> + <property name="text"> + <string>Limit encryption to TLS 1.0</string> + </property> + </widget> + </item> + <item> <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/Swift/QtUI/QtConnectionSettingsWindow.cpp b/Swift/QtUI/QtConnectionSettingsWindow.cpp index a3598fa..7b5003a 100644 --- a/Swift/QtUI/QtConnectionSettingsWindow.cpp +++ b/Swift/QtUI/QtConnectionSettingsWindow.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Isode Limited. + * Copyright (c) 2012-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -67,6 +67,7 @@ QtConnectionSettingsWindow::QtConnectionSettingsWindow(const ClientOptions& opti isDefault &= options.proxyType == defaults.proxyType; isDefault &= options.manualProxyHostname == defaults.manualProxyHostname; isDefault &= options.manualProxyPort == defaults.manualProxyPort; + isDefault &= options.tlsOptions.schannelTLS1_0Workaround == defaults.tlsOptions.schannelTLS1_0Workaround; if (isDefault) { ui.connectionMethod->setCurrentIndex(0); } @@ -88,6 +89,7 @@ QtConnectionSettingsWindow::QtConnectionSettingsWindow(const ClientOptions& opti ui.manual_manualProxyHost->setText(P2QSTRING(options.manualProxyHostname)); ui.manual_manualProxyPort->setText(P2QSTRING(boost::lexical_cast<std::string>(options.manualProxyPort))); } + ui.manual_forceTLS1_0->setChecked(options.tlsOptions.schannelTLS1_0Workaround); } } else { ui.connectionMethod->setCurrentIndex(2); @@ -100,6 +102,9 @@ QtConnectionSettingsWindow::QtConnectionSettingsWindow(const ClientOptions& opti } } } +#ifndef HAVE_SCHANNEL + ui.manual_forceTLS1_0->hide(); +#endif } void QtConnectionSettingsWindow::handleProxyTypeChanged(int index) { @@ -129,6 +134,7 @@ ClientOptions QtConnectionSettingsWindow::getOptions() { options.useTLS = static_cast<ClientOptions::UseTLS>(ui.manual_useTLS->currentIndex()); options.useStreamCompression = ui.manual_allowCompression->isChecked(); options.allowPLAINWithoutTLS = ui.manual_allowPLAINWithoutTLS->isChecked(); + options.tlsOptions.schannelTLS1_0Workaround = ui.manual_forceTLS1_0->isChecked(); if (ui.manual_manualHost->isChecked()) { options.manualHostname = Q2PSTRING(ui.manual_manualHostName->text()); try { diff --git a/Swiften/Client/ClientOptions.h b/Swiften/Client/ClientOptions.h index 4aac609..25393e4 100644 --- a/Swiften/Client/ClientOptions.h +++ b/Swiften/Client/ClientOptions.h @@ -11,6 +11,8 @@ #include <Swiften/Base/API.h> #include <Swiften/Base/URL.h> #include <Swiften/Base/SafeString.h> +#include <Swiften/TLS/TLSOptions.h> + namespace Swift { class HTTPTrafficFilter; @@ -145,5 +147,10 @@ namespace Swift { * proxy initialization to be customized. */ boost::shared_ptr<HTTPTrafficFilter> httpTrafficFilter; + + /** + * Options passed to the TLS stack + */ + TLSOptions tlsOptions; }; } diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp index 842488d..c91e5c5 100644 --- a/Swiften/Client/CoreClient.cpp +++ b/Swiften/Client/CoreClient.cpp @@ -138,7 +138,8 @@ void CoreClient::connect(const ClientOptions& o) { host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, - options.boshHTTPConnectProxyAuthPassword)); + options.boshHTTPConnectProxyAuthPassword, + options.tlsOptions)); sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1)); bindSessionToStream(); @@ -189,7 +190,7 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connectio connection_ = connection; assert(!sessionStream_); - sessionStream_ = boost::make_shared<BasicSessionStream>(ClientStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory()); + sessionStream_ = boost::make_shared<BasicSessionStream>(ClientStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory(), options.tlsOptions); if (certificate_ && !certificate_->isNull()) { sessionStream_->setTLSCertificate(certificate_); } diff --git a/Swiften/Component/CoreComponent.cpp b/Swiften/Component/CoreComponent.cpp index 23b7759..358b0c6 100644 --- a/Swiften/Component/CoreComponent.cpp +++ b/Swiften/Component/CoreComponent.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -9,14 +9,16 @@ #include <boost/bind.hpp> #include <iostream> +#include <Swiften/Base/IDGenerator.h> #include <Swiften/Component/ComponentSession.h> +#include <Swiften/Component/ComponentSessionStanzaChannel.h> #include <Swiften/Network/Connector.h> #include <Swiften/Network/NetworkFactories.h> -#include <Swiften/TLS/PKCS12Certificate.h> -#include <Swiften/Session/BasicSessionStream.h> #include <Swiften/Queries/IQRouter.h> -#include <Swiften/Base/IDGenerator.h> -#include <Swiften/Component/ComponentSessionStanzaChannel.h> +#include <Swiften/Session/BasicSessionStream.h> +#include <Swiften/TLS/PKCS12Certificate.h> +#include <Swiften/TLS/TLSOptions.h> + namespace Swift { @@ -63,7 +65,7 @@ void CoreComponent::handleConnectorFinished(boost::shared_ptr<Connection> connec connection_ = connection; assert(!sessionStream_); - sessionStream_ = boost::shared_ptr<BasicSessionStream>(new BasicSessionStream(ComponentStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), NULL, networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory())); + sessionStream_ = boost::shared_ptr<BasicSessionStream>(new BasicSessionStream(ComponentStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), NULL, networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory(), TLSOptions())); sessionStream_->onDataRead.connect(boost::bind(&CoreComponent::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&CoreComponent::handleDataWritten, this, _1)); diff --git a/Swiften/Network/BOSHConnectionPool.cpp b/Swiften/Network/BOSHConnectionPool.cpp index 56f7d12..c037b34 100644 --- a/Swiften/Network/BOSHConnectionPool.cpp +++ b/Swiften/Network/BOSHConnectionPool.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -17,7 +17,7 @@ #include <Swiften/Network/CachingDomainNameResolver.h> namespace Swift { -BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* realResolver, ConnectionFactory* connectionFactoryParameter, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword) : +BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* realResolver, ConnectionFactory* connectionFactoryParameter, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword, const TLSOptions& tlsOptions) : boshURL(boshURL), connectionFactory(connectionFactoryParameter), xmlParserFactory(parserFactory), @@ -31,13 +31,13 @@ BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* r if (!boshHTTPConnectProxyURL.isEmpty()) { if (boshHTTPConnectProxyURL.getScheme() == "https") { - connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory); + connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory, tlsOptions); myConnectionFactories.push_back(connectionFactory); } connectionFactory = new HTTPConnectProxiedConnectionFactory(realResolver, connectionFactory, timerFactory, boshHTTPConnectProxyURL.getHost(), URL::getPortOrDefaultPort(boshHTTPConnectProxyURL), boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword); } if (boshURL.getScheme() == "https") { - connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory); + connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory, tlsOptions); myConnectionFactories.push_back(connectionFactory); } resolver = new CachingDomainNameResolver(realResolver, eventLoop); diff --git a/Swiften/Network/BOSHConnectionPool.h b/Swiften/Network/BOSHConnectionPool.h index 570ba4b..d845a3d 100644 --- a/Swiften/Network/BOSHConnectionPool.h +++ b/Swiften/Network/BOSHConnectionPool.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -12,6 +12,8 @@ #include <Swiften/Base/API.h> #include <Swiften/Base/SafeString.h> #include <Swiften/Network/BOSHConnection.h> +#include <Swiften/TLS/TLSOptions.h> + namespace Swift { class HTTPConnectProxiedConnectionFactory; @@ -21,7 +23,7 @@ namespace Swift { class SWIFTEN_API BOSHConnectionPool : public boost::bsignals::trackable { public: - BOSHConnectionPool(const URL& boshURL, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword); + BOSHConnectionPool(const URL& boshURL, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword, const TLSOptions& tlsOptions); ~BOSHConnectionPool(); void write(const SafeByteArray& data); void writeFooter(); diff --git a/Swiften/Network/TLSConnection.cpp b/Swiften/Network/TLSConnection.cpp index f0b6fa4..149548a 100644 --- a/Swiften/Network/TLSConnection.cpp +++ b/Swiften/Network/TLSConnection.cpp @@ -14,8 +14,8 @@ namespace Swift { -TLSConnection::TLSConnection(Connection::ref connection, TLSContextFactory* tlsFactory) : connection(connection) { - context = tlsFactory->createTLSContext(); +TLSConnection::TLSConnection(Connection::ref connection, TLSContextFactory* tlsFactory, const TLSOptions& tlsOptions) : connection(connection) { + context = tlsFactory->createTLSContext(tlsOptions); context->onDataForNetwork.connect(boost::bind(&TLSConnection::handleTLSDataForNetwork, this, _1)); context->onDataForApplication.connect(boost::bind(&TLSConnection::handleTLSDataForApplication, this, _1)); context->onConnected.connect(boost::bind(&TLSConnection::handleTLSConnectFinished, this, false)); diff --git a/Swiften/Network/TLSConnection.h b/Swiften/Network/TLSConnection.h index ebf2e43..96525ad 100644 --- a/Swiften/Network/TLSConnection.h +++ b/Swiften/Network/TLSConnection.h @@ -13,6 +13,8 @@ #include <Swiften/Base/API.h> #include <Swiften/Base/SafeByteArray.h> #include <Swiften/Network/Connection.h> +#include <Swiften/TLS/TLSOptions.h> + namespace Swift { class HostAddressPort; @@ -22,7 +24,7 @@ namespace Swift { class SWIFTEN_API TLSConnection : public Connection { public: - TLSConnection(Connection::ref connection, TLSContextFactory* tlsFactory); + TLSConnection(Connection::ref connection, TLSContextFactory* tlsFactory, const TLSOptions&); virtual ~TLSConnection(); virtual void listen() {assert(false);} diff --git a/Swiften/Network/TLSConnectionFactory.cpp b/Swiften/Network/TLSConnectionFactory.cpp index ac0ab8e..cc20b2d 100644 --- a/Swiften/Network/TLSConnectionFactory.cpp +++ b/Swiften/Network/TLSConnectionFactory.cpp @@ -12,7 +12,7 @@ namespace Swift { -TLSConnectionFactory::TLSConnectionFactory(TLSContextFactory* contextFactory, ConnectionFactory* connectionFactory) : contextFactory(contextFactory), connectionFactory(connectionFactory){ +TLSConnectionFactory::TLSConnectionFactory(TLSContextFactory* contextFactory, ConnectionFactory* connectionFactory, const TLSOptions& o) : contextFactory(contextFactory), connectionFactory(connectionFactory), options_(o) { } @@ -22,7 +22,7 @@ TLSConnectionFactory::~TLSConnectionFactory() { boost::shared_ptr<Connection> TLSConnectionFactory::createConnection() { - return boost::make_shared<TLSConnection>(connectionFactory->createConnection(), contextFactory); + return boost::make_shared<TLSConnection>(connectionFactory->createConnection(), contextFactory, options_); } } diff --git a/Swiften/Network/TLSConnectionFactory.h b/Swiften/Network/TLSConnectionFactory.h index 3dfee06..0c67014 100644 --- a/Swiften/Network/TLSConnectionFactory.h +++ b/Swiften/Network/TLSConnectionFactory.h @@ -11,18 +11,20 @@ #include <Swiften/Base/API.h> #include <Swiften/Network/ConnectionFactory.h> #include <Swiften/TLS/TLSContextFactory.h> +#include <Swiften/TLS/TLSOptions.h> namespace Swift { class Connection; class SWIFTEN_API TLSConnectionFactory : public ConnectionFactory { public: - TLSConnectionFactory(TLSContextFactory* contextFactory, ConnectionFactory* connectionFactory); + TLSConnectionFactory(TLSContextFactory* contextFactory, ConnectionFactory* connectionFactory, const TLSOptions&); virtual ~TLSConnectionFactory(); virtual boost::shared_ptr<Connection> createConnection(); private: TLSContextFactory* contextFactory; ConnectionFactory* connectionFactory; + TLSOptions options_; }; } diff --git a/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp b/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp index e5ac121..e687517 100644 --- a/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp +++ b/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp @@ -16,15 +16,16 @@ #include <boost/lexical_cast.hpp> #include <Swiften/Base/Algorithm.h> -#include <Swiften/Network/Connection.h> -#include <Swiften/Network/ConnectionFactory.h> +#include <Swiften/EventLoop/DummyEventLoop.h> #include <Swiften/Network/BOSHConnection.h> #include <Swiften/Network/BOSHConnectionPool.h> +#include <Swiften/Network/Connection.h> +#include <Swiften/Network/ConnectionFactory.h> +#include <Swiften/Network/DummyTimerFactory.h> #include <Swiften/Network/HostAddressPort.h> #include <Swiften/Network/StaticDomainNameResolver.h> -#include <Swiften/Network/DummyTimerFactory.h> -#include <Swiften/EventLoop/DummyEventLoop.h> #include <Swiften/Parser/PlatformXMLParserFactory.h> +#include <Swiften/TLS/TLSOptions.h> @@ -322,7 +323,7 @@ class BOSHConnectionPoolTest : public CppUnit::TestFixture { private: PoolRef createTestling() { - BOSHConnectionPool* a = new BOSHConnectionPool(boshURL, resolver, connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString("")); + BOSHConnectionPool* a = new BOSHConnectionPool(boshURL, resolver, connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString(""), TLSOptions()); PoolRef pool(a); //FIXME: Remko - why does the above work, but the below fail? //PoolRef pool = boost::make_shared<BOSHConnectionPool>(boshURL, resolver, connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString("")); diff --git a/Swiften/Session/BOSHSessionStream.cpp b/Swiften/Session/BOSHSessionStream.cpp index eac493e..62942b9 100644 --- a/Swiften/Session/BOSHSessionStream.cpp +++ b/Swiften/Session/BOSHSessionStream.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -39,7 +39,8 @@ BOSHSessionStream::BOSHSessionStream( const std::string& to, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, - const SafeString& boshHTTPConnectProxyAuthPassword) : + const SafeString& boshHTTPConnectProxyAuthPassword, + const TLSOptions& tlsOptions) : available(false), eventLoop(eventLoop), firstHeader(true) { @@ -49,7 +50,7 @@ BOSHSessionStream::BOSHSessionStream( random.seed(static_cast<unsigned int>(time(NULL))); unsigned long long initialRID = boost::variate_generator<boost::mt19937&, boost::uniform_int<unsigned long long> >(random, dist)(); - connectionPool = new BOSHConnectionPool(boshURL, resolver, connectionFactory, xmlParserFactory, tlsContextFactory, timerFactory, eventLoop, to, initialRID, boshHTTPConnectProxyURL, boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword); + connectionPool = new BOSHConnectionPool(boshURL, resolver, connectionFactory, xmlParserFactory, tlsContextFactory, timerFactory, eventLoop, to, initialRID, boshHTTPConnectProxyURL, boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword, tlsOptions); connectionPool->onSessionTerminated.connect(boost::bind(&BOSHSessionStream::handlePoolSessionTerminated, this, _1)); connectionPool->onSessionStarted.connect(boost::bind(&BOSHSessionStream::handlePoolSessionStarted, this)); connectionPool->onXMPPDataRead.connect(boost::bind(&BOSHSessionStream::handlePoolXMPPDataRead, this, _1)); diff --git a/Swiften/Session/BOSHSessionStream.h b/Swiften/Session/BOSHSessionStream.h index e97436c..436b941 100644 --- a/Swiften/Session/BOSHSessionStream.h +++ b/Swiften/Session/BOSHSessionStream.h @@ -9,12 +9,14 @@ #include <boost/shared_ptr.hpp> #include <Swiften/Base/API.h> -#include <Swiften/Base/SafeString.h> #include <Swiften/Base/SafeByteArray.h> -#include <Swiften/Network/BOSHConnectionPool.h> -#include <Swiften/Session/SessionStream.h> +#include <Swiften/Base/SafeString.h> #include <Swiften/Elements/StreamType.h> #include <Swiften/EventLoop/EventOwner.h> +#include <Swiften/Network/BOSHConnectionPool.h> +#include <Swiften/Session/SessionStream.h> +#include <Swiften/TLS/TLSOptions.h> + namespace Swift { class TimerFactory; @@ -43,7 +45,8 @@ namespace Swift { const std::string& to, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, - const SafeString& boshHTTPConnectProxyAuthPassword + const SafeString& boshHTTPConnectProxyAuthPassword, + const TLSOptions& tlsOptions ); ~BOSHSessionStream(); diff --git a/Swiften/Session/BasicSessionStream.cpp b/Swiften/Session/BasicSessionStream.cpp index 274d218..43f1c48 100644 --- a/Swiften/Session/BasicSessionStream.cpp +++ b/Swiften/Session/BasicSessionStream.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -27,14 +27,16 @@ BasicSessionStream::BasicSessionStream( PayloadSerializerCollection* payloadSerializers, TLSContextFactory* tlsContextFactory, TimerFactory* timerFactory, - XMLParserFactory* xmlParserFactory) : + XMLParserFactory* xmlParserFactory, + const TLSOptions& tlsOptions) : available(false), connection(connection), tlsContextFactory(tlsContextFactory), timerFactory(timerFactory), compressionLayer(NULL), tlsLayer(NULL), - whitespacePingLayer(NULL) { + whitespacePingLayer(NULL), + tlsOptions_(tlsOptions) { xmppLayer = new XMPPLayer(payloadParserFactories, payloadSerializers, xmlParserFactory, streamType); xmppLayer->onStreamStart.connect(boost::bind(&BasicSessionStream::handleStreamStartReceived, this, _1)); xmppLayer->onElement.connect(boost::bind(&BasicSessionStream::handleElementReceived, this, _1)); @@ -106,7 +108,7 @@ bool BasicSessionStream::supportsTLSEncryption() { void BasicSessionStream::addTLSEncryption() { assert(available); - tlsLayer = new TLSLayer(tlsContextFactory); + tlsLayer = new TLSLayer(tlsContextFactory, tlsOptions_); if (hasTLSCertificate() && !tlsLayer->setClientCertificate(getTLSCertificate())) { onClosed(boost::make_shared<SessionStreamError>(SessionStreamError::InvalidTLSCertificateError)); } diff --git a/Swiften/Session/BasicSessionStream.h b/Swiften/Session/BasicSessionStream.h index 7832e42..811374a 100644 --- a/Swiften/Session/BasicSessionStream.h +++ b/Swiften/Session/BasicSessionStream.h @@ -14,6 +14,7 @@ #include <Swiften/Session/SessionStream.h> #include <Swiften/Elements/StreamType.h> #include <Swiften/TLS/TLSError.h> +#include <Swiften/TLS/TLSOptions.h> namespace Swift { class TLSContextFactory; @@ -37,7 +38,8 @@ namespace Swift { PayloadSerializerCollection* payloadSerializers, TLSContextFactory* tlsContextFactory, TimerFactory* whitespacePingLayerFactory, - XMLParserFactory* xmlParserFactory + XMLParserFactory* xmlParserFactory, + const TLSOptions& tlsOptions ); ~BasicSessionStream(); @@ -86,6 +88,7 @@ namespace Swift { TLSLayer* tlsLayer; WhitespacePingLayer* whitespacePingLayer; StreamStack* streamStack; + TLSOptions tlsOptions_; }; } diff --git a/Swiften/StreamStack/TLSLayer.cpp b/Swiften/StreamStack/TLSLayer.cpp index aebf4a2..15c4101 100644 --- a/Swiften/StreamStack/TLSLayer.cpp +++ b/Swiften/StreamStack/TLSLayer.cpp @@ -13,8 +13,8 @@ namespace Swift { -TLSLayer::TLSLayer(TLSContextFactory* factory) { - context = factory->createTLSContext(); +TLSLayer::TLSLayer(TLSContextFactory* factory, const TLSOptions& tlsOptions) { + context = factory->createTLSContext(tlsOptions); context->onDataForNetwork.connect(boost::bind(&TLSLayer::writeDataToChildLayer, this, _1)); context->onDataForApplication.connect(boost::bind(&TLSLayer::writeDataToParentLayer, this, _1)); context->onConnected.connect(onConnected); diff --git a/Swiften/StreamStack/TLSLayer.h b/Swiften/StreamStack/TLSLayer.h index 089512d..87d69a9 100644 --- a/Swiften/StreamStack/TLSLayer.h +++ b/Swiften/StreamStack/TLSLayer.h @@ -15,6 +15,7 @@ #include <Swiften/TLS/CertificateWithKey.h> #include <Swiften/TLS/CertificateVerificationError.h> #include <Swiften/TLS/TLSError.h> +#include <Swiften/TLS/TLSOptions.h> namespace Swift { class TLSContext; @@ -22,7 +23,7 @@ namespace Swift { class SWIFTEN_API TLSLayer : public StreamLayer { public: - TLSLayer(TLSContextFactory*); + TLSLayer(TLSContextFactory*, const TLSOptions&); ~TLSLayer(); void connect(); diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp index 5fbc913..50f6731 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp @@ -14,7 +14,7 @@ bool OpenSSLContextFactory::canCreate() const { return true; } -TLSContext* OpenSSLContextFactory::createTLSContext() { +TLSContext* OpenSSLContextFactory::createTLSContext(const TLSOptions&) { return new OpenSSLContext(); } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h index 8e689b5..bf7f08a 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h +++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h @@ -14,7 +14,7 @@ namespace Swift { class OpenSSLContextFactory : public TLSContextFactory { public: bool canCreate() const; - virtual TLSContext* createTLSContext(); + virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions); // Not supported virtual void setCheckCertificateRevocation(bool b); diff --git a/Swiften/TLS/Schannel/SchannelContext.cpp b/Swiften/TLS/Schannel/SchannelContext.cpp index 86b8c18..5f230ec 100644 --- a/Swiften/TLS/Schannel/SchannelContext.cpp +++ b/Swiften/TLS/Schannel/SchannelContext.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2012 Isode Limited. + * Copyright (c) 2012-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -21,8 +21,8 @@ namespace Swift { //------------------------------------------------------------------------ -SchannelContext::SchannelContext() : m_state(Start), m_secContext(0), m_my_cert_store(NULL), m_cert_store_name("MY"), m_cert_name(), m_smartcard_reader(), checkCertificateRevocation(true) { - m_ctxtFlags = ISC_REQ_ALLOCATE_MEMORY | +SchannelContext::SchannelContext(bool tls1_0Workaround) : state_(Start), secContext_(0), myCertStore_(NULL), certStoreName_("MY"), certName_(), smartCardReader_(), checkCertificateRevocation_(true), tls1_0Workaround_(tls1_0Workaround) { + contextFlags_ = ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR | ISC_REQ_INTEGRITY | @@ -31,19 +31,19 @@ SchannelContext::SchannelContext() : m_state(Start), m_secContext(0), m_my_cert_ ISC_REQ_USE_SUPPLIED_CREDS | ISC_REQ_STREAM; - ZeroMemory(&m_streamSizes, sizeof(m_streamSizes)); + ZeroMemory(&streamSizes_, sizeof(streamSizes_)); } //------------------------------------------------------------------------ SchannelContext::~SchannelContext() { - if (m_my_cert_store) CertCloseStore(m_my_cert_store, 0); + if (myCertStore_) CertCloseStore(myCertStore_, 0); } //------------------------------------------------------------------------ void SchannelContext::determineStreamSizes() { - QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_STREAM_SIZES, &m_streamSizes); + QueryContextAttributes(contextHandle_, SECPKG_ATTR_STREAM_SIZES, &streamSizes_); } //------------------------------------------------------------------------ @@ -51,20 +51,20 @@ void SchannelContext::determineStreamSizes() { void SchannelContext::connect() { ScopedCertContext pCertContext; - m_state = Connecting; + state_ = Connecting; // If a user name is specified, then attempt to find a client // certificate. Otherwise, just create a NULL credential. - if (!m_cert_name.empty()) { - if (m_my_cert_store == NULL) { - m_my_cert_store = CertOpenSystemStore(0, m_cert_store_name.c_str()); - if (!m_my_cert_store) { + if (!certName_.empty()) { + if (myCertStore_ == NULL) { + myCertStore_ = CertOpenSystemStore(0, certStoreName_.c_str()); + if (!myCertStore_) { indicateError(boost::make_shared<TLSError>(TLSError::UnknownError)); return; } } - pCertContext = findCertificateInStore( m_my_cert_store, m_cert_name ); + pCertContext = findCertificateInStore( myCertStore_, certName_ ); if (pCertContext == NULL) { indicateError(boost::make_shared<TLSError>(TLSError::UnknownError)); return; @@ -77,8 +77,13 @@ void SchannelContext::connect() { SCHANNEL_CRED sc = {0}; sc.dwVersion = SCHANNEL_CRED_VERSION; -/////SSL3? - sc.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT; + if (tls1_0Workaround_) { + sc.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT; + } + else { + sc.grbitEnabledProtocols = /*SP_PROT_SSL3_CLIENT | */SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT; + } + sc.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION; if (pCertContext) { @@ -103,9 +108,9 @@ void SchannelContext::connect() { &sc, NULL, NULL, - m_credHandle.Reset(), + credHandle_.Reset(), NULL); - + if (status != SEC_E_OK) { // We failed to obtain the credentials handle indicateError(boost::make_shared<TLSError>(TLSError::UnknownError)); @@ -135,17 +140,17 @@ void SchannelContext::connect() { // Create the initial security context status = InitializeSecurityContext( - m_credHandle, + credHandle_, NULL, NULL, - m_ctxtFlags, + contextFlags_, 0, 0, NULL, 0, - m_ctxtHandle.Reset(), + contextHandle_.Reset(), &outBufferDesc, - &m_secContext, + &secContext_, NULL); if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { @@ -164,7 +169,7 @@ void SchannelContext::connect() { handleCertError(status); } - m_state = Connected; + state_ = Connected; determineStreamSizes(); onConnected(); @@ -179,7 +184,7 @@ SECURITY_STATUS SchannelContext::validateServerCertificate() { return SEC_E_WRONG_PRINCIPAL; } - const LPSTR usage[] = + const LPSTR usage[] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, @@ -193,7 +198,7 @@ SECURITY_STATUS SchannelContext::validateServerCertificate() { chainParams.RequestedUsage.Usage.rgpszUsageIdentifier = const_cast<LPSTR*>(usage); DWORD chainFlags = CERT_CHAIN_CACHE_END_CERT; - if (checkCertificateRevocation) { + if (checkCertificateRevocation_) { chainFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; } @@ -246,9 +251,9 @@ SECURITY_STATUS SchannelContext::validateServerCertificate() { //------------------------------------------------------------------------ void SchannelContext::appendNewData(const SafeByteArray& data) { - size_t originalSize = m_receivedData.size(); - m_receivedData.resize(originalSize + data.size()); - memcpy(&m_receivedData[0] + originalSize, &data[0], data.size()); + size_t originalSize = receivedData_.size(); + receivedData_.resize(originalSize + data.size()); + memcpy(&receivedData_[0] + originalSize, &data[0], data.size()); } //------------------------------------------------------------------------ @@ -256,12 +261,12 @@ void SchannelContext::appendNewData(const SafeByteArray& data) { void SchannelContext::continueHandshake(const SafeByteArray& data) { appendNewData(data); - while (!m_receivedData.empty()) { + while (!receivedData_.empty()) { SecBuffer inBuffers[2]; // Provide Schannel with the remote host's handshake data - inBuffers[0].pvBuffer = (char*)(&m_receivedData[0]); - inBuffers[0].cbBuffer = (unsigned long)m_receivedData.size(); + inBuffers[0].pvBuffer = (char*)(&receivedData_[0]); + inBuffers[0].cbBuffer = (unsigned long)receivedData_.size(); inBuffers[0].BufferType = SECBUFFER_TOKEN; inBuffers[1].pvBuffer = NULL; @@ -295,17 +300,17 @@ void SchannelContext::continueHandshake(const SafeByteArray& data) { outBufferDesc.ulVersion = SECBUFFER_VERSION; SECURITY_STATUS status = InitializeSecurityContext( - m_credHandle, - m_ctxtHandle, + credHandle_, + contextHandle_, NULL, - m_ctxtFlags, + contextFlags_, 0, 0, &inBufferDesc, 0, NULL, &outBufferDesc, - &m_secContext, + &secContext_, NULL); if (status == SEC_E_INCOMPLETE_MESSAGE) { @@ -315,16 +320,16 @@ void SchannelContext::continueHandshake(const SafeByteArray& data) { else if (status == SEC_I_CONTINUE_NEEDED) { SecBuffer* pDataBuffer = &outBuffers[0]; SecBuffer* pExtraBuffer = &inBuffers[1]; - + if (pDataBuffer && pDataBuffer->cbBuffer > 0 && pDataBuffer->pvBuffer != NULL) { sendDataOnNetwork(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); } if (pExtraBuffer->BufferType == SECBUFFER_EXTRA) { - m_receivedData.erase(m_receivedData.begin(), m_receivedData.end() - pExtraBuffer->cbBuffer); + receivedData_.erase(receivedData_.begin(), receivedData_.end() - pExtraBuffer->cbBuffer); } else { - m_receivedData.clear(); + receivedData_.clear(); } break; @@ -336,19 +341,19 @@ void SchannelContext::continueHandshake(const SafeByteArray& data) { } SecBuffer* pExtraBuffer = &inBuffers[1]; - + if (pExtraBuffer && pExtraBuffer->cbBuffer > 0) { - m_receivedData.erase(m_receivedData.begin(), m_receivedData.end() - pExtraBuffer->cbBuffer); + receivedData_.erase(receivedData_.begin(), receivedData_.end() - pExtraBuffer->cbBuffer); } else { - m_receivedData.clear(); + receivedData_.clear(); } - m_state = Connected; + state_ = Connected; determineStreamSizes(); onConnected(); - } + } else { // We failed to initialize the security context handleCertError(status); @@ -360,35 +365,35 @@ void SchannelContext::continueHandshake(const SafeByteArray& data) { //------------------------------------------------------------------------ -void SchannelContext::handleCertError(SECURITY_STATUS status) +void SchannelContext::handleCertError(SECURITY_STATUS status) { - if (status == SEC_E_UNTRUSTED_ROOT || + if (status == SEC_E_UNTRUSTED_ROOT || status == CERT_E_UNTRUSTEDROOT || - status == CRYPT_E_ISSUER_SERIALNUMBER || + status == CRYPT_E_ISSUER_SERIALNUMBER || status == CRYPT_E_SIGNER_NOT_FOUND || status == CRYPT_E_NO_TRUSTED_SIGNER) { - m_verificationError = CertificateVerificationError::Untrusted; + verificationError_ = CertificateVerificationError::Untrusted; } - else if (status == SEC_E_CERT_EXPIRED || + else if (status == SEC_E_CERT_EXPIRED || status == CERT_E_EXPIRED) { - m_verificationError = CertificateVerificationError::Expired; + verificationError_ = CertificateVerificationError::Expired; } else if (status == CRYPT_E_SELF_SIGNED) { - m_verificationError = CertificateVerificationError::SelfSigned; + verificationError_ = CertificateVerificationError::SelfSigned; } else if (status == CRYPT_E_HASH_VALUE || status == TRUST_E_CERT_SIGNATURE) { - m_verificationError = CertificateVerificationError::InvalidSignature; + verificationError_ = CertificateVerificationError::InvalidSignature; } else if (status == CRYPT_E_REVOKED) { - m_verificationError = CertificateVerificationError::Revoked; + verificationError_ = CertificateVerificationError::Revoked; } else if (status == CRYPT_E_NO_REVOCATION_CHECK || status == CRYPT_E_REVOCATION_OFFLINE) { - m_verificationError = CertificateVerificationError::RevocationCheckFailed; + verificationError_ = CertificateVerificationError::RevocationCheckFailed; } else { - m_verificationError = CertificateVerificationError::UnknownError; + verificationError_ = CertificateVerificationError::UnknownError; } } @@ -416,7 +421,7 @@ void SchannelContext::forwardDataToApplication(const void* pData, size_t dataSiz void SchannelContext::handleDataFromApplication(const SafeByteArray& data) { // Don't attempt to send data until we're fully connected - if (m_state == Connecting) { + if (state_ == Connecting) { return; } @@ -427,7 +432,7 @@ void SchannelContext::handleDataFromApplication(const SafeByteArray& data) { //------------------------------------------------------------------------ void SchannelContext::handleDataFromNetwork(const SafeByteArray& data) { - switch (m_state) { + switch (state_) { case Connecting: { // We're still establishing the connection, so continue the handshake @@ -450,8 +455,8 @@ void SchannelContext::handleDataFromNetwork(const SafeByteArray& data) { //------------------------------------------------------------------------ void SchannelContext::indicateError(boost::shared_ptr<TLSError> error) { - m_state = Error; - m_receivedData.clear(); + state_ = Error; + receivedData_.clear(); onError(error); } @@ -461,20 +466,20 @@ void SchannelContext::decryptAndProcessData(const SafeByteArray& data) { SecBuffer inBuffers[4] = {0}; appendNewData(data); - - while (!m_receivedData.empty()) { + + while (!receivedData_.empty()) { // - // MSDN: - // When using the Schannel SSP with contexts that are not connection oriented, on input, - // the structure must contain four SecBuffer structures. Exactly one buffer must be of type - // SECBUFFER_DATA and contain an encrypted message, which is decrypted in place. The remaining - // buffers are used for output and must be of type SECBUFFER_EMPTY. For connection-oriented - // contexts, a SECBUFFER_DATA type buffer must be supplied, as noted for nonconnection-oriented - // contexts. Additionally, a second SECBUFFER_TOKEN type buffer that contains a security token + // MSDN: + // When using the Schannel SSP with contexts that are not connection oriented, on input, + // the structure must contain four SecBuffer structures. Exactly one buffer must be of type + // SECBUFFER_DATA and contain an encrypted message, which is decrypted in place. The remaining + // buffers are used for output and must be of type SECBUFFER_EMPTY. For connection-oriented + // contexts, a SECBUFFER_DATA type buffer must be supplied, as noted for nonconnection-oriented + // contexts. Additionally, a second SECBUFFER_TOKEN type buffer that contains a security token // must also be supplied. // - inBuffers[0].pvBuffer = (char*)(&m_receivedData[0]); - inBuffers[0].cbBuffer = (unsigned long)m_receivedData.size(); + inBuffers[0].pvBuffer = (char*)(&receivedData_[0]); + inBuffers[0].cbBuffer = (unsigned long)receivedData_.size(); inBuffers[0].BufferType = SECBUFFER_DATA; inBuffers[1].BufferType = SECBUFFER_EMPTY; @@ -486,22 +491,22 @@ void SchannelContext::decryptAndProcessData(const SafeByteArray& data) { inBufferDesc.pBuffers = inBuffers; inBufferDesc.ulVersion = SECBUFFER_VERSION; - size_t inData = m_receivedData.size(); - SECURITY_STATUS status = DecryptMessage(m_ctxtHandle, &inBufferDesc, 0, NULL); + size_t inData = receivedData_.size(); + SECURITY_STATUS status = DecryptMessage(contextHandle_, &inBufferDesc, 0, NULL); if (status == SEC_E_INCOMPLETE_MESSAGE) { // Wait for more data to arrive break; - } + } else if (status == SEC_I_RENEGOTIATE) { // TODO: Handle renegotiation scenarios indicateError(boost::make_shared<TLSError>(TLSError::UnknownError)); break; - } + } else if (status == SEC_I_CONTEXT_EXPIRED) { indicateError(boost::make_shared<TLSError>(TLSError::UnknownError)); break; - } + } else if (status != SEC_E_OK) { indicateError(boost::make_shared<TLSError>(TLSError::UnknownError)); break; @@ -524,11 +529,11 @@ void SchannelContext::decryptAndProcessData(const SafeByteArray& data) { // If there is extra data left over from the decryption operation, we call DecryptMessage() again if (pExtraBuffer) { - m_receivedData.erase(m_receivedData.begin(), m_receivedData.end() - pExtraBuffer->cbBuffer); - } + receivedData_.erase(receivedData_.begin(), receivedData_.end() - pExtraBuffer->cbBuffer); + } else { // We're done - m_receivedData.erase(m_receivedData.begin(), m_receivedData.begin() + inData); + receivedData_.erase(receivedData_.begin(), receivedData_.begin() + inData); } } } @@ -536,43 +541,43 @@ void SchannelContext::decryptAndProcessData(const SafeByteArray& data) { //------------------------------------------------------------------------ void SchannelContext::encryptAndSendData(const SafeByteArray& data) { - if (m_streamSizes.cbMaximumMessage == 0) { + if (streamSizes_.cbMaximumMessage == 0) { return; } SecBuffer outBuffers[4] = {0}; // Calculate the largest required size of the send buffer - size_t messageBufferSize = (data.size() > m_streamSizes.cbMaximumMessage) - ? m_streamSizes.cbMaximumMessage + size_t messageBufferSize = (data.size() > streamSizes_.cbMaximumMessage) + ? streamSizes_.cbMaximumMessage : data.size(); // Allocate a packet for the encrypted data SafeByteArray sendBuffer; - sendBuffer.resize(m_streamSizes.cbHeader + messageBufferSize + m_streamSizes.cbTrailer); + sendBuffer.resize(streamSizes_.cbHeader + messageBufferSize + streamSizes_.cbTrailer); size_t bytesSent = 0; do { size_t bytesLeftToSend = data.size() - bytesSent; // Calculate how much of the send buffer we'll be using for this chunk - size_t bytesToSend = (bytesLeftToSend > m_streamSizes.cbMaximumMessage) - ? m_streamSizes.cbMaximumMessage + size_t bytesToSend = (bytesLeftToSend > streamSizes_.cbMaximumMessage) + ? streamSizes_.cbMaximumMessage : bytesLeftToSend; - + // Copy the plain text data into the send buffer - memcpy(&sendBuffer[0] + m_streamSizes.cbHeader, &data[0] + bytesSent, bytesToSend); + memcpy(&sendBuffer[0] + streamSizes_.cbHeader, &data[0] + bytesSent, bytesToSend); outBuffers[0].pvBuffer = &sendBuffer[0]; - outBuffers[0].cbBuffer = m_streamSizes.cbHeader; + outBuffers[0].cbBuffer = streamSizes_.cbHeader; outBuffers[0].BufferType = SECBUFFER_STREAM_HEADER; - outBuffers[1].pvBuffer = &sendBuffer[0] + m_streamSizes.cbHeader; + outBuffers[1].pvBuffer = &sendBuffer[0] + streamSizes_.cbHeader; outBuffers[1].cbBuffer = (unsigned long)bytesToSend; outBuffers[1].BufferType = SECBUFFER_DATA; - outBuffers[2].pvBuffer = &sendBuffer[0] + m_streamSizes.cbHeader + bytesToSend; - outBuffers[2].cbBuffer = m_streamSizes.cbTrailer; + outBuffers[2].pvBuffer = &sendBuffer[0] + streamSizes_.cbHeader + bytesToSend; + outBuffers[2].cbBuffer = streamSizes_.cbTrailer; outBuffers[2].BufferType = SECBUFFER_STREAM_TRAILER; outBuffers[3].pvBuffer = 0; @@ -584,7 +589,7 @@ void SchannelContext::encryptAndSendData(const SafeByteArray& data) { outBufferDesc.pBuffers = outBuffers; outBufferDesc.ulVersion = SECBUFFER_VERSION; - SECURITY_STATUS status = EncryptMessage(m_ctxtHandle, 0, &outBufferDesc, 0); + SECURITY_STATUS status = EncryptMessage(contextHandle_, 0, &outBufferDesc, 0); if (status != SEC_E_OK) { indicateError(boost::make_shared<TLSError>(TLSError::UnknownError)); return; @@ -604,14 +609,14 @@ bool SchannelContext::setClientCertificate(CertificateWithKey::ref certificate) return false; } - userCertificate = capiCertificate; + userCertificate_ = capiCertificate; // We assume that the Certificate Store Name/Certificate Name // are valid at this point - m_cert_store_name = capiCertificate->getCertStoreName(); - m_cert_name = capiCertificate->getCertName(); + certStoreName_ = capiCertificate->getCertStoreName(); + certName_ = capiCertificate->getCertName(); ////At the moment this is only useful for logging: - m_smartcard_reader = capiCertificate->getSmartCardReaderName(); + smartCardReader_ = capiCertificate->getSmartCardReaderName(); capiCertificate->onCertificateCardRemoved.connect(boost::bind(&SchannelContext::handleCertificateCardRemoved, this)); @@ -630,7 +635,7 @@ std::vector<Certificate::ref> SchannelContext::getPeerCertificateChain() const { ScopedCertContext pServerCert; ScopedCertContext pIssuerCert; ScopedCertContext pCurrentCert; - SECURITY_STATUS status = QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset()); + SECURITY_STATUS status = QueryContextAttributes(contextHandle_, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset()); if (status != SEC_E_OK) { return certificateChain; @@ -655,14 +660,14 @@ std::vector<Certificate::ref> SchannelContext::getPeerCertificateChain() const { //------------------------------------------------------------------------ CertificateVerificationError::ref SchannelContext::getPeerCertificateVerificationError() const { - return m_verificationError ? boost::make_shared<CertificateVerificationError>(*m_verificationError) : CertificateVerificationError::ref(); + return verificationError_ ? boost::make_shared<CertificateVerificationError>(*verificationError_) : CertificateVerificationError::ref(); } //------------------------------------------------------------------------ ByteArray SchannelContext::getFinishMessage() const { SecPkgContext_Bindings bindings; - int ret = QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_UNIQUE_BINDINGS, &bindings); + int ret = QueryContextAttributes(contextHandle_, SECPKG_ATTR_UNIQUE_BINDINGS, &bindings); if (ret == SEC_E_OK) { return createByteArray(((unsigned char*) bindings.Bindings) + bindings.Bindings->dwApplicationDataOffset + 11 /* tls-unique:*/, bindings.Bindings->cbApplicationDataLength - 11); } @@ -672,7 +677,7 @@ ByteArray SchannelContext::getFinishMessage() const { //------------------------------------------------------------------------ void SchannelContext::setCheckCertificateRevocation(bool b) { - checkCertificateRevocation = b; + checkCertificateRevocation_ = b; } diff --git a/Swiften/TLS/Schannel/SchannelContext.h b/Swiften/TLS/Schannel/SchannelContext.h index 3a068f2..19cc473 100644 --- a/Swiften/TLS/Schannel/SchannelContext.h +++ b/Swiften/TLS/Schannel/SchannelContext.h @@ -37,7 +37,7 @@ namespace Swift typedef boost::shared_ptr<SchannelContext> sp_t; public: - SchannelContext(); + SchannelContext(bool tls1_0Workaround); ~SchannelContext(); @@ -86,23 +86,24 @@ namespace Swift }; - SchannelState m_state; - boost::optional<CertificateVerificationError> m_verificationError; + SchannelState state_; + boost::optional<CertificateVerificationError> verificationError_; - ULONG m_secContext; - ScopedCredHandle m_credHandle; - ScopedCtxtHandle m_ctxtHandle; - DWORD m_ctxtFlags; - SecPkgContext_StreamSizes m_streamSizes; + ULONG secContext_; + ScopedCredHandle credHandle_; + ScopedCtxtHandle contextHandle_; + DWORD contextFlags_; + SecPkgContext_StreamSizes streamSizes_; - std::vector<char> m_receivedData; + std::vector<char> receivedData_; - HCERTSTORE m_my_cert_store; - std::string m_cert_store_name; - std::string m_cert_name; + HCERTSTORE myCertStore_; + std::string certStoreName_; + std::string certName_; ////Not needed, most likely - std::string m_smartcard_reader; //Can be empty string for non SmartCard certificates - boost::shared_ptr<CAPICertificate> userCertificate; - bool checkCertificateRevocation; + std::string smartCardReader_; //Can be empty string for non SmartCard certificates + boost::shared_ptr<CAPICertificate> userCertificate_; + bool checkCertificateRevocation_; + bool tls1_0Workaround_; }; } diff --git a/Swiften/TLS/Schannel/SchannelContextFactory.cpp b/Swiften/TLS/Schannel/SchannelContextFactory.cpp index 8b0044c..6e83b0d 100644 --- a/Swiften/TLS/Schannel/SchannelContextFactory.cpp +++ b/Swiften/TLS/Schannel/SchannelContextFactory.cpp @@ -4,6 +4,12 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #include "Swiften/TLS/Schannel/SchannelContextFactory.h" #include "Swiften/TLS/Schannel/SchannelContext.h" @@ -16,8 +22,8 @@ bool SchannelContextFactory::canCreate() const { return true; } -TLSContext* SchannelContextFactory::createTLSContext() { - SchannelContext* context = new SchannelContext(); +TLSContext* SchannelContextFactory::createTLSContext(const TLSOptions& tlsOptions) { + SchannelContext* context = new SchannelContext(tlsOptions.schannelTLS1_0Workaround); context->setCheckCertificateRevocation(checkCertificateRevocation); return context; } diff --git a/Swiften/TLS/Schannel/SchannelContextFactory.h b/Swiften/TLS/Schannel/SchannelContextFactory.h index 9dc835c..789d15f 100644 --- a/Swiften/TLS/Schannel/SchannelContextFactory.h +++ b/Swiften/TLS/Schannel/SchannelContextFactory.h @@ -4,9 +4,15 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #pragma once -#include "Swiften/TLS/TLSContextFactory.h" +#include <Swiften/TLS/TLSContextFactory.h> namespace Swift { class SchannelContextFactory : public TLSContextFactory { @@ -14,7 +20,7 @@ namespace Swift { SchannelContextFactory(); bool canCreate() const; - virtual TLSContext* createTLSContext(); + virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions); virtual void setCheckCertificateRevocation(bool b); diff --git a/Swiften/TLS/TLSContextFactory.h b/Swiften/TLS/TLSContextFactory.h index 10c5577..90da4a1 100644 --- a/Swiften/TLS/TLSContextFactory.h +++ b/Swiften/TLS/TLSContextFactory.h @@ -7,6 +7,7 @@ #pragma once #include <Swiften/Base/API.h> +#include <Swiften/TLS/TLSOptions.h> namespace Swift { class TLSContext; @@ -17,7 +18,7 @@ namespace Swift { virtual bool canCreate() const = 0; - virtual TLSContext* createTLSContext() = 0; + virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions) = 0; virtual void setCheckCertificateRevocation(bool b) = 0; }; } diff --git a/Swiften/TLS/TLSOptions.h b/Swiften/TLS/TLSOptions.h new file mode 100644 index 0000000..ca84829 --- /dev/null +++ b/Swiften/TLS/TLSOptions.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +namespace Swift { + + struct TLSOptions { + TLSOptions() : schannelTLS1_0Workaround(false) { + + } + + /** + * A bug in the Windows SChannel TLS stack, combined with + * overly-restrictive server stacks means it's sometimes necessary to + * not use TLS>1.0. This option has no effect unless compiled on + * Windows against SChannel (OpenSSL users are unaffected). + */ + bool schannelTLS1_0Workaround; + + }; +} -- cgit v0.10.2-6-g49f6