diff options
27 files changed, 248 insertions, 162 deletions
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 @@ -395,25 +395,25 @@ void MainController::handleConnected() { client_->getDiscoManager()->setCapsNode(CLIENT_NODE); client_->getDiscoManager()->setDiscoInfo(discoInfo); 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); client_->requestRoster(); GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(JID(boundJID_.getDomain()), client_->getIQRouter()); discoInfoRequest->onResponse.connect(boost::bind(&MainController::handleServerDiscoInfoResponse, this, _1, _2)); discoInfoRequest->send(); client_->getVCardManager()->requestOwnVCard(); - + rosterController_->setJID(boundJID_); rosterController_->setEnabled(true); rosterController_->getWindow()->setStreamEncryptionStatus(client_->isStreamEncrypted()); profileController_->setAvailable(true); contactEditController_->setAvailable(true); /* Send presence later to catch all the incoming presences. */ @@ -838,16 +838,17 @@ std::string MainController::serializeClientOptions(const ClientOptions& options) SERIALIZE_STRING(manualProxyHostname); SERIALIZE_INT(manualProxyPort); SERIALIZE_URL(boshURL); 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++; #define PARSE_BOOL(option, defaultValue) PARSE_INT_RAW(defaultValue); result.option = (intVal == 1); #define PARSE_INT(option, defaultValue) PARSE_INT_RAW(defaultValue); result.option = intVal; #define PARSE_STRING(option) PARSE_STRING_RAW; result.option = stringVal; @@ -885,11 +886,12 @@ ClientOptions MainController::parseClientOptions(const std::string& optionString PARSE_STRING(manualProxyHostname); PARSE_INT(manualProxyPort, -1); PARSE_URL(boshURL); 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 @@ -133,12 +133,19 @@ <property name="text"> <string>Allow sending password over insecure connection</string> </property> </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> </property> <property name="sizeType"> <enum>QSizePolicy::Fixed</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,8 +1,8 @@ /* - * Copyright (c) 2012 Isode Limited. + * Copyright (c) 2012-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include "Swift/QtUI/QtConnectionSettingsWindow.h" @@ -64,12 +64,13 @@ QtConnectionSettingsWindow::QtConnectionSettingsWindow(const ClientOptions& opti isDefault &= options.useAcks == defaults.useAcks; isDefault &= options.manualHostname == defaults.manualHostname; isDefault &= options.manualPort == defaults.manualPort; 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); } else { ui.connectionMethod->setCurrentIndex(1); ui.manual_useTLS->setCurrentIndex(options.useTLS); @@ -85,24 +86,28 @@ QtConnectionSettingsWindow::QtConnectionSettingsWindow(const ClientOptions& opti ui.manual_proxyType->setCurrentIndex(options.proxyType); if (!options.manualProxyHostname.empty()) { ui.manual_manualProxy->setChecked(true); 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); ui.bosh_uri->setText(P2QSTRING(options.boshURL.toString())); if (!options.boshHTTPConnectProxyURL.isEmpty()) { ui.bosh_manualProxy->setChecked(true); ui.bosh_manualProxyHost->setText(P2QSTRING(options.boshHTTPConnectProxyURL.getHost())); if (options.boshHTTPConnectProxyURL.getPort()) { ui.bosh_manualProxyPort->setText(P2QSTRING(boost::lexical_cast<std::string>(*options.boshHTTPConnectProxyURL.getPort()))); } } } +#ifndef HAVE_SCHANNEL + ui.manual_forceTLS1_0->hide(); +#endif } void QtConnectionSettingsWindow::handleProxyTypeChanged(int index) { bool proxySettingsVisible = index != NoProxy && index != SystemProxy; ui.manual_manualProxy->setVisible(proxySettingsVisible); ui.manual_manualProxyHostLabel->setVisible(proxySettingsVisible); @@ -126,12 +131,13 @@ ClientOptions QtConnectionSettingsWindow::getOptions() { /* Not automatic */ if (ui.connectionMethod->currentIndex() == 1) { /* Manual */ 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 { options.manualPort = boost::lexical_cast<int>(Q2PSTRING(ui.manual_manualHostPort->text())); } catch (const boost::bad_lexical_cast&) { options.manualPort = -1; 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 @@ -8,12 +8,14 @@ #include <boost/shared_ptr.hpp> #include <Swiften/Base/API.h> #include <Swiften/Base/URL.h> #include <Swiften/Base/SafeString.h> +#include <Swiften/TLS/TLSOptions.h> + namespace Swift { class HTTPTrafficFilter; struct SWIFTEN_API ClientOptions { enum UseTLS { @@ -142,8 +144,13 @@ namespace Swift { /** * This can be initialized with a custom HTTPTrafficFilter, which allows HTTP CONNECT * 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 @@ -135,13 +135,14 @@ void CoreClient::connect(const ClientOptions& o) { networkFactories->getXMLParserFactory(), networkFactories->getEventLoop(), networkFactories->getDomainNameResolver(), 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(); } } @@ -186,13 +187,13 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connectio } else { assert(!connection_); 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_); } sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1)); 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,25 +1,27 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/Component/CoreComponent.h> #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 { CoreComponent::CoreComponent(const JID& jid, const std::string& secret, NetworkFactories* networkFactories) : networkFactories(networkFactories), jid_(jid), secret_(secret), disconnectRequested_(false) { stanzaChannel_ = new ComponentSessionStanzaChannel(); stanzaChannel_->onMessageReceived.connect(boost::ref(onMessageReceived)); @@ -60,13 +62,13 @@ void CoreComponent::handleConnectorFinished(boost::shared_ptr<Connection> connec } else { assert(!connection_); 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)); session_ = ComponentSession::create(jid_, secret_, sessionStream_, networkFactories->getCryptoProvider()); stanzaChannel_->setSession(session_); session_->onFinished.connect(boost::bind(&CoreComponent::handleSessionFinished, 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,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/BOSHConnectionPool.h> #include <climits> @@ -14,13 +14,13 @@ #include <Swiften/Base/SafeString.h> #include <Swiften/Network/TLSConnectionFactory.h> #include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h> #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), timerFactory(timerFactory), rid(initialRID), pendingTerminate(false), @@ -28,19 +28,19 @@ BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* r requestLimit(2), restartCount(0), pendingRestart(false) { 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); createConnection(); } 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,30 +1,32 @@ /* - * Copyright (c) 2011 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once #include <vector> #include <Swiften/Base/API.h> #include <Swiften/Base/SafeString.h> #include <Swiften/Network/BOSHConnection.h> +#include <Swiften/TLS/TLSOptions.h> + namespace Swift { class HTTPConnectProxiedConnectionFactory; class TLSConnectionFactory; class CachingDomainNameResolver; class EventLoop; 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(); void close(); void restartStream(); 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 @@ -11,14 +11,14 @@ #include <Swiften/Network/HostAddressPort.h> #include <Swiften/TLS/TLSContext.h> #include <Swiften/TLS/TLSContextFactory.h> 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)); context->onError.connect(boost::bind(&TLSConnection::handleTLSConnectFinished, this, true)); connection->onConnectFinished.connect(boost::bind(&TLSConnection::handleRawConnectFinished, this, _1)); 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 @@ -10,22 +10,24 @@ #include <boost/enable_shared_from_this.hpp> #include <Swiften/Base/boost_bsignals.h> #include <Swiften/Base/API.h> #include <Swiften/Base/SafeByteArray.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 { public: - TLSConnection(Connection::ref connection, TLSContextFactory* tlsFactory); + TLSConnection(Connection::ref connection, TLSContextFactory* tlsFactory, const TLSOptions&); virtual ~TLSConnection(); virtual void listen() {assert(false);} virtual void connect(const HostAddressPort& address); virtual void disconnect(); virtual void write(const SafeByteArray& data); 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 @@ -9,20 +9,20 @@ #include <boost/shared_ptr.hpp> #include <Swiften/Network/TLSConnection.h> 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) { } 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 @@ -8,21 +8,23 @@ #include <boost/shared_ptr.hpp> #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 @@ -13,21 +13,22 @@ #include <boost/bind.hpp> #include <boost/smart_ptr/make_shared.hpp> #include <boost/shared_ptr.hpp> #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> using namespace Swift; typedef boost::shared_ptr<BOSHConnectionPool> PoolRef; @@ -319,13 +320,13 @@ 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("")); pool->onXMPPDataRead.connect(boost::bind(&BOSHConnectionPoolTest::handleXMPPDataRead, this, _1)); pool->onBOSHDataRead.connect(boost::bind(&BOSHConnectionPoolTest::handleBOSHDataRead, this, _1)); pool->onBOSHDataWritten.connect(boost::bind(&BOSHConnectionPoolTest::handleBOSHDataWritten, this, _1)); 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,8 +1,8 @@ /* - * Copyright (c) 2011-2014 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/Session/BOSHSessionStream.h> @@ -36,23 +36,24 @@ BOSHSessionStream::BOSHSessionStream( XMLParserFactory* xmlParserFactory, EventLoop* eventLoop, DomainNameResolver* resolver, 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) { boost::mt19937 random; boost::uniform_int<unsigned long long> dist(0, (1LL<<53) - 1); 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)); connectionPool->onBOSHDataRead.connect(boost::bind(&BOSHSessionStream::handlePoolBOSHDataRead, this, _1)); connectionPool->onBOSHDataWritten.connect(boost::bind(&BOSHSessionStream::handlePoolBOSHDataWritten, 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 @@ -6,18 +6,20 @@ #pragma once #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; class PayloadParserFactoryCollection; class PayloadSerializerCollection; class StreamStack; @@ -40,13 +42,14 @@ namespace Swift { XMLParserFactory* xmlParserFactory, EventLoop* eventLoop, DomainNameResolver* resolver, const std::string& to, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, - const SafeString& boshHTTPConnectProxyAuthPassword + const SafeString& boshHTTPConnectProxyAuthPassword, + const TLSOptions& tlsOptions ); ~BOSHSessionStream(); virtual void close(); virtual bool isOpen(); 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,8 +1,8 @@ /* - * Copyright (c) 2010-2014 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/Session/BasicSessionStream.h> @@ -24,20 +24,22 @@ BasicSessionStream::BasicSessionStream( StreamType streamType, boost::shared_ptr<Connection> connection, PayloadParserFactoryCollection* payloadParserFactories, 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)); xmppLayer->onError.connect(boost::bind(&BasicSessionStream::handleXMPPError, this)); xmppLayer->onDataRead.connect(boost::bind(&BasicSessionStream::handleDataRead, this, _1)); xmppLayer->onWriteData.connect(boost::bind(&BasicSessionStream::handleDataWritten, this, _1)); @@ -103,13 +105,13 @@ bool BasicSessionStream::isOpen() { bool BasicSessionStream::supportsTLSEncryption() { return tlsContextFactory && tlsContextFactory->canCreate(); } 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)); } else { streamStack->addLayer(tlsLayer); tlsLayer->onError.connect(boost::bind(&BasicSessionStream::handleTLSError, this, _1)); 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 @@ -11,12 +11,13 @@ #include <Swiften/Base/API.h> #include <Swiften/Base/SafeByteArray.h> #include <Swiften/Network/Connection.h> #include <Swiften/Session/SessionStream.h> #include <Swiften/Elements/StreamType.h> #include <Swiften/TLS/TLSError.h> +#include <Swiften/TLS/TLSOptions.h> namespace Swift { class TLSContextFactory; class TLSLayer; class TimerFactory; class WhitespacePingLayer; @@ -34,13 +35,14 @@ namespace Swift { StreamType streamType, boost::shared_ptr<Connection> connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, TLSContextFactory* tlsContextFactory, TimerFactory* whitespacePingLayerFactory, - XMLParserFactory* xmlParserFactory + XMLParserFactory* xmlParserFactory, + const TLSOptions& tlsOptions ); ~BasicSessionStream(); virtual void close(); virtual bool isOpen(); @@ -83,9 +85,10 @@ namespace Swift { XMPPLayer* xmppLayer; ConnectionLayer* connectionLayer; CompressionLayer* compressionLayer; 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 @@ -10,14 +10,14 @@ #include <Swiften/TLS/TLSContextFactory.h> #include <Swiften/TLS/TLSContext.h> 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); context->onError.connect(onError); } 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 @@ -12,20 +12,21 @@ #include <Swiften/Base/SafeByteArray.h> #include <Swiften/StreamStack/StreamLayer.h> #include <Swiften/TLS/Certificate.h> #include <Swiften/TLS/CertificateWithKey.h> #include <Swiften/TLS/CertificateVerificationError.h> #include <Swiften/TLS/TLSError.h> +#include <Swiften/TLS/TLSOptions.h> namespace Swift { class TLSContext; class TLSContextFactory; class SWIFTEN_API TLSLayer : public StreamLayer { public: - TLSLayer(TLSContextFactory*); + TLSLayer(TLSContextFactory*, const TLSOptions&); ~TLSLayer(); void connect(); bool setClientCertificate(CertificateWithKey::ref cert); Certificate::ref getPeerCertificate() const; 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 @@ -11,13 +11,13 @@ namespace Swift { bool OpenSSLContextFactory::canCreate() const { return true; } -TLSContext* OpenSSLContextFactory::createTLSContext() { +TLSContext* OpenSSLContextFactory::createTLSContext(const TLSOptions&) { return new OpenSSLContext(); } void OpenSSLContextFactory::setCheckCertificateRevocation(bool check) { if (check) { assert(false); 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 @@ -11,12 +11,12 @@ #include <cassert> 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 @@ -2,13 +2,13 @@ * Copyright (c) 2011 Soren Dreijer * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ /* - * Copyright (c) 2012 Isode Limited. + * Copyright (c) 2012-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <boost/bind.hpp> @@ -18,70 +18,75 @@ #include <WinHTTP.h> /* For SECURITY_FLAG_IGNORE_CERT_CN_INVALID */ 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 | ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | 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_); } //------------------------------------------------------------------------ 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; } } // We use an empty list for client certificates PCCERT_CONTEXT clientCerts[1] = {0}; 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) { sc.cCreds = 1; sc.paCred = pCertContext.GetPointer(); sc.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; @@ -100,15 +105,15 @@ void SchannelContext::connect() { UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &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)); return; } @@ -132,23 +137,23 @@ void SchannelContext::connect() { outBufferDesc.cBuffers = 2; outBufferDesc.pBuffers = outBuffers; outBufferDesc.ulVersion = SECBUFFER_VERSION; // 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) { // We failed to initialize the security context handleCertError(status); indicateError(boost::make_shared<TLSError>(TLSError::UnknownError)); @@ -161,13 +166,13 @@ void SchannelContext::connect() { if (status == SEC_E_OK) { status = validateServerCertificate(); if (status != SEC_E_OK) { handleCertError(status); } - m_state = Connected; + state_ = Connected; determineStreamSizes(); onConnected(); } } @@ -176,13 +181,13 @@ void SchannelContext::connect() { SECURITY_STATUS SchannelContext::validateServerCertificate() { SchannelCertificate::ref pServerCert = boost::dynamic_pointer_cast<SchannelCertificate>( getPeerCertificate() ); if (!pServerCert) { return SEC_E_WRONG_PRINCIPAL; } - const LPSTR usage[] = + const LPSTR usage[] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE }; @@ -190,13 +195,13 @@ SECURITY_STATUS SchannelContext::validateServerCertificate() { chainParams.cbSize = sizeof(chainParams); chainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; chainParams.RequestedUsage.Usage.cUsageIdentifier = ARRAYSIZE(usage); 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; } ScopedCertChainContext pChainContext; BOOL success = CertGetCertificateChain( @@ -243,28 +248,28 @@ SECURITY_STATUS SchannelContext::validateServerCertificate() { return S_OK; } //------------------------------------------------------------------------ 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()); } //------------------------------------------------------------------------ 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; inBuffers[1].cbBuffer = 0; inBuffers[1].BufferType = SECBUFFER_EMPTY; @@ -292,106 +297,106 @@ void SchannelContext::continueHandshake(const SafeByteArray& data) { SecBufferDesc outBufferDesc = {0}; outBufferDesc.cBuffers = 2; outBufferDesc.pBuffers = outBuffers; 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) { // Wait for more data to arrive break; } 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; } else if (status == SEC_E_OK) { status = validateServerCertificate(); if (status != SEC_E_OK) { handleCertError(status); } 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); indicateError(boost::make_shared<TLSError>(TLSError::UnknownError)); return; } } } //------------------------------------------------------------------------ -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; } } //------------------------------------------------------------------------ void SchannelContext::sendDataOnNetwork(const void* pData, size_t dataSize) { @@ -413,24 +418,24 @@ 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; } // Encrypt the data encryptAndSendData(data); } //------------------------------------------------------------------------ void SchannelContext::handleDataFromNetwork(const SafeByteArray& data) { - switch (m_state) { + switch (state_) { case Connecting: { // We're still establishing the connection, so continue the handshake continueHandshake(data); } break; @@ -447,64 +452,64 @@ 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); } //------------------------------------------------------------------------ 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; inBuffers[2].BufferType = SECBUFFER_EMPTY; inBuffers[3].BufferType = SECBUFFER_EMPTY; SecBufferDesc inBufferDesc = {0}; inBufferDesc.cBuffers = 4; 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; } SecBuffer* pDataBuffer = NULL; @@ -521,73 +526,73 @@ void SchannelContext::decryptAndProcessData(const SafeByteArray& data) { if (pDataBuffer && pDataBuffer->cbBuffer > 0 && pDataBuffer->pvBuffer != NULL) { forwardDataToApplication(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer); } // 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); } } } //------------------------------------------------------------------------ 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; outBuffers[3].cbBuffer = 0; outBuffers[3].BufferType = SECBUFFER_EMPTY; SecBufferDesc outBufferDesc = {0}; outBufferDesc.cBuffers = 4; 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; } sendDataOnNetwork(&sendBuffer[0], outBuffers[0].cbBuffer + outBuffers[1].cbBuffer + outBuffers[2].cbBuffer); @@ -601,20 +606,20 @@ void SchannelContext::encryptAndSendData(const SafeByteArray& data) { bool SchannelContext::setClientCertificate(CertificateWithKey::ref certificate) { boost::shared_ptr<CAPICertificate> capiCertificate = boost::dynamic_pointer_cast<CAPICertificate>(certificate); if (!capiCertificate || capiCertificate->isNull()) { 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)); return true; } @@ -627,13 +632,13 @@ void SchannelContext::handleCertificateCardRemoved() { std::vector<Certificate::ref> SchannelContext::getPeerCertificateChain() const { std::vector<Certificate::ref> certificateChain; 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; } certificateChain.push_back(boost::make_shared<SchannelCertificate>(pServerCert)); @@ -652,28 +657,28 @@ std::vector<Certificate::ref> SchannelContext::getPeerCertificateChain() const { return certificateChain; } //------------------------------------------------------------------------ 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); } return ByteArray(); } //------------------------------------------------------------------------ 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 @@ -34,13 +34,13 @@ namespace Swift class SchannelContext : public TLSContext, boost::noncopyable { public: typedef boost::shared_ptr<SchannelContext> sp_t; public: - SchannelContext(); + SchannelContext(bool tls1_0Workaround); ~SchannelContext(); // // TLSContext // @@ -83,26 +83,27 @@ namespace Swift Connecting, Connected, Error }; - 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 @@ -1,26 +1,32 @@ /* * Copyright (c) 2011 Soren Dreijer * Licensed under the simplified BSD license. * 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" namespace Swift { SchannelContextFactory::SchannelContextFactory() : checkCertificateRevocation(true) { } 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; } void SchannelContextFactory::setCheckCertificateRevocation(bool b) { checkCertificateRevocation = b; 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 @@ -1,23 +1,29 @@ /* * Copyright (c) 2011 Soren Dreijer * Licensed under the simplified BSD license. * 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 { public: SchannelContextFactory(); bool canCreate() const; - virtual TLSContext* createTLSContext(); + virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions); virtual void setCheckCertificateRevocation(bool b); public: bool checkCertificateRevocation; }; 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 @@ -4,20 +4,21 @@ * See the COPYING file for more information. */ #pragma once #include <Swiften/Base/API.h> +#include <Swiften/TLS/TLSOptions.h> namespace Swift { class TLSContext; class SWIFTEN_API TLSContextFactory { public: virtual ~TLSContextFactory(); 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; + + }; +} |