diff options
author | Tobias Markmann <tm@ayena.de> | 2015-10-19 14:23:42 (GMT) |
---|---|---|
committer | Swift Review <review@swift.im> | 2015-11-10 18:28:39 (GMT) |
commit | ab651834c9088e34e6e66eb105e0fb855f1572c4 (patch) | |
tree | 611d434fedcb69f6698af9dc4696e59fba9d97b7 | |
parent | 9b090062d0270d556c9253e0ddf767d7c5bbadbf (diff) | |
download | swift-ab651834c9088e34e6e66eb105e0fb855f1572c4.zip swift-ab651834c9088e34e6e66eb105e0fb855f1572c4.tar.bz2 |
Add support for client certificate authentication in BOSH
This allows to authenticate using SASL EXTERNAL over BOSH
using a client TLS certificate for the HTTPS connection of
the BOSH channel.
The implementation also enforces the HTTPS server certificate
of subsequent BOSH connections not to change.
This commit also removes TLSConnection and TLSConnectionFactory
as no code is using them.
Test-Information:
Tested against M-Link 16.3v6-0 on Debian 7.9 and Swift on
OS X 10.10.5. Verified working client certificate authentication.
Verified Swift not falling back to password-based authentication,
in case EXTERNAL is not allowed by the server over BOSH or
the client certificate is invalid.
Change-Id: Ia96bcac27cac9fc9261ed847c82c6328307bfbd1
-rw-r--r-- | Swiften/Client/CoreClient.cpp | 39 | ||||
-rw-r--r-- | Swiften/Network/BOSHConnection.cpp | 104 | ||||
-rw-r--r-- | Swiften/Network/BOSHConnection.h | 58 | ||||
-rw-r--r-- | Swiften/Network/BOSHConnectionPool.cpp | 58 | ||||
-rw-r--r-- | Swiften/Network/BOSHConnectionPool.h | 18 | ||||
-rw-r--r-- | Swiften/Network/BoostConnection.h | 9 | ||||
-rw-r--r-- | Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp | 23 | ||||
-rw-r--r-- | Swiften/Network/UnitTest/BOSHConnectionTest.cpp | 5 | ||||
-rw-r--r-- | Swiften/QA/TLSTest/CertificateErrorTest.cpp | 3 | ||||
-rw-r--r-- | Swiften/Session/BOSHSessionStream.cpp | 13 | ||||
-rw-r--r-- | Swiften/Session/BOSHSessionStream.h | 1 | ||||
-rw-r--r-- | Swiften/StreamStack/DummyStreamLayer.h | 30 | ||||
-rw-r--r-- | Swiften/StreamStack/LowLayer.h | 3 |
13 files changed, 286 insertions, 78 deletions
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp index af64577..6fc491c 100644 --- a/Swiften/Client/CoreClient.cpp +++ b/Swiften/Client/CoreClient.cpp @@ -124,25 +124,30 @@ void CoreClient::connect(const ClientOptions& o) { /* Autodiscovery of which proxy works is largely ok with a TCP session, because this is a one-off. With BOSH * it would be quite painful given that potentially every stanza could be sent on a new connection. */ - //sessionStream_ = boost::make_shared<BOSHSessionStream>(boost::make_shared<BOSHConnectionFactory>(options.boshURL, networkFactories->getConnectionFactory(), networkFactories->getXMLParserFactory(), networkFactories->getTLSContextFactory()), getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory(), networkFactories->getEventLoop(), host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, options.boshHTTPConnectProxyAuthPassword); - sessionStream_ = boost::shared_ptr<BOSHSessionStream>(new BOSHSessionStream( - options.boshURL, - getPayloadParserFactories(), - getPayloadSerializers(), - networkFactories->getConnectionFactory(), - networkFactories->getTLSContextFactory(), - networkFactories->getTimerFactory(), - networkFactories->getXMLParserFactory(), - networkFactories->getEventLoop(), - networkFactories->getDomainNameResolver(), - host, - options.boshHTTPConnectProxyURL, - options.boshHTTPConnectProxyAuthID, - options.boshHTTPConnectProxyAuthPassword, - options.tlsOptions, - options.httpTrafficFilter)); + boost::shared_ptr<BOSHSessionStream> boshSessionStream_ = boost::shared_ptr<BOSHSessionStream>(new BOSHSessionStream( + options.boshURL, + getPayloadParserFactories(), + getPayloadSerializers(), + networkFactories->getConnectionFactory(), + networkFactories->getTLSContextFactory(), + networkFactories->getTimerFactory(), + networkFactories->getXMLParserFactory(), + networkFactories->getEventLoop(), + networkFactories->getDomainNameResolver(), + host, + options.boshHTTPConnectProxyURL, + options.boshHTTPConnectProxyAuthID, + options.boshHTTPConnectProxyAuthPassword, + options.tlsOptions, + options.httpTrafficFilter)); + sessionStream_ = boshSessionStream_; sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1)); + if (certificate_ && !certificate_->isNull()) { + SWIFT_LOG(debug) << "set certificate" << std::endl; + sessionStream_->setTLSCertificate(certificate_); + } + boshSessionStream_->open(); bindSessionToStream(); } diff --git a/Swiften/Network/BOSHConnection.cpp b/Swiften/Network/BOSHConnection.cpp index 298b015..9a836cb 100644 --- a/Swiften/Network/BOSHConnection.cpp +++ b/Swiften/Network/BOSHConnection.cpp @@ -24,10 +24,14 @@ #include <Swiften/Base/String.h> #include <Swiften/Network/HostAddressPort.h> #include <Swiften/Parser/BOSHBodyExtractor.h> +#include <Swiften/StreamStack/DummyStreamLayer.h> +#include <Swiften/StreamStack/TLSLayer.h> +#include <Swiften/TLS/TLSContext.h> +#include <Swiften/TLS/TLSOptions.h> namespace Swift { -BOSHConnection::BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory) +BOSHConnection::BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory, TLSContextFactory* tlsContextFactory, const TLSOptions& tlsOptions) : boshURL_(boshURL), connector_(connector), parserFactory_(parserFactory), @@ -37,6 +41,12 @@ BOSHConnection::BOSHConnection(const URL& boshURL, Connector::ref connector, XML pending_(false), connectionReady_(false) { + if (boshURL_.getScheme() == "https") { + tlsLayer_ = boost::make_shared<TLSLayer>(tlsContextFactory, tlsOptions); + // The following dummyLayer_ is needed as the TLSLayer will pass the decrypted data to its parent layer. + // The dummyLayer_ will serve as the parent layer. + dummyLayer_ = boost::make_shared<DummyStreamLayer>(tlsLayer_.get()); + } } BOSHConnection::~BOSHConnection() { @@ -61,6 +71,39 @@ void BOSHConnection::cancelConnector() { } } +void BOSHConnection::handleTLSConnected() { + SWIFT_LOG(debug) << std::endl; + onConnectFinished(false); +} + +void BOSHConnection::handleTLSApplicationDataRead(const SafeByteArray& data) { + SWIFT_LOG(debug) << std::endl; + handleDataRead(boost::make_shared<SafeByteArray>(data)); +} + +void BOSHConnection::handleTLSNetowrkDataWriteRequest(const SafeByteArray& data) { + SWIFT_LOG(debug) << std::endl; + connection_->write(data); +} + +void BOSHConnection::handleRawDataRead(boost::shared_ptr<SafeByteArray> data) { + SWIFT_LOG(debug) << std::endl; + tlsLayer_->handleDataRead(*data.get()); +} + +void BOSHConnection::handleTLSError(boost::shared_ptr<TLSError> error) { + +} + +void BOSHConnection::writeData(const SafeByteArray& data) { + if (tlsLayer_) { + tlsLayer_->writeData(data); + } + else { + connection_->write(data); + } +} + void BOSHConnection::disconnect() { if (connection_) { connection_->disconnect(); @@ -76,6 +119,40 @@ void BOSHConnection::restartStream() { write(createSafeByteArray(""), true, false); } +bool BOSHConnection::setClientCertificate(CertificateWithKey::ref cert) { + if (tlsLayer_) { + SWIFT_LOG(debug) << "set client certificate" << std::endl; + return tlsLayer_->setClientCertificate(cert); + } + else { + return false; + } +} + +Certificate::ref BOSHConnection::getPeerCertificate() const { + Certificate::ref peerCertificate; + if (tlsLayer_) { + peerCertificate = tlsLayer_->getPeerCertificate(); + } + return peerCertificate; +} + +std::vector<Certificate::ref> BOSHConnection::getPeerCertificateChain() const { + std::vector<Certificate::ref> peerCertificateChain; + if (tlsLayer_) { + peerCertificateChain = tlsLayer_->getPeerCertificateChain(); + } + return peerCertificateChain; +} + +CertificateVerificationError::ref BOSHConnection::getPeerCertificateVerificationError() const { + CertificateVerificationError::ref verificationError; + if (tlsLayer_) { + verificationError = tlsLayer_->getPeerCertificateVerificationError(); + } + return verificationError; +} + void BOSHConnection::terminateStream() { write(createSafeByteArray(""), false, true); } @@ -129,7 +206,7 @@ void BOSHConnection::write(const SafeByteArray& data, bool streamRestart, bool t SafeByteArray safeHeader = createHTTPRequest(data, streamRestart, terminate, rid_, sid_, boshURL_).first; onBOSHDataWritten(safeHeader); - connection_->write(safeHeader); + writeData(safeHeader); pending_ = true; SWIFT_LOG(debug) << "write data: " << safeByteArrayToString(safeHeader) << std::endl; @@ -140,10 +217,25 @@ void BOSHConnection::handleConnectFinished(Connection::ref connection) { connectionReady_ = !!connection; if (connectionReady_) { connection_ = connection; - connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); - connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); + if (tlsLayer_) { + connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleRawDataRead, shared_from_this(), _1)); + connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); + + tlsLayer_->getContext()->onDataForNetwork.connect(boost::bind(&BOSHConnection::handleTLSNetowrkDataWriteRequest, shared_from_this(), _1)); + tlsLayer_->getContext()->onDataForApplication.connect(boost::bind(&BOSHConnection::handleTLSApplicationDataRead, shared_from_this(), _1)); + tlsLayer_->onConnected.connect(boost::bind(&BOSHConnection::handleTLSConnected, shared_from_this())); + tlsLayer_->onError.connect(boost::bind(&BOSHConnection::handleTLSError, shared_from_this(), _1)); + tlsLayer_->connect(); + } + else { + connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); + connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); + } + } + + if (!connectionReady_ || !tlsLayer_) { + onConnectFinished(!connectionReady_); } - onConnectFinished(!connectionReady_); } void BOSHConnection::startStream(const std::string& to, unsigned long long rid) { @@ -180,7 +272,7 @@ void BOSHConnection::startStream(const std::string& to, unsigned long long rid) waitingForStartResponse_ = true; SafeByteArray safeHeader = createSafeByteArray(header.str()); onBOSHDataWritten(safeHeader); - connection_->write(safeHeader); + writeData(safeHeader); SWIFT_LOG(debug) << "write stream header: " << safeByteArrayToString(safeHeader) << std::endl; } diff --git a/Swiften/Network/BOSHConnection.h b/Swiften/Network/BOSHConnection.h index 65d179a..da43e71 100644 --- a/Swiften/Network/BOSHConnection.h +++ b/Swiften/Network/BOSHConnection.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2011 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -16,13 +16,14 @@ #include <boost/enable_shared_from_this.hpp> #include <Swiften/Base/API.h> +#include <Swiften/Base/Error.h> +#include <Swiften/Base/String.h> +#include <Swiften/Base/URL.h> #include <Swiften/Network/Connection.h> #include <Swiften/Network/Connector.h> #include <Swiften/Network/HostAddressPort.h> -#include <Swiften/Base/String.h> -#include <Swiften/Base/URL.h> -#include <Swiften/Base/Error.h> #include <Swiften/Session/SessionStream.h> +#include <Swiften/TLS/TLSError.h> namespace boost { class thread; @@ -36,27 +37,31 @@ class BOSHConnectionTest; namespace Swift { class XMLParserFactory; class TLSContextFactory; + class TLSLayer; + struct TLSOptions; + class HighLayer; - class SWIFTEN_API BOSHError : public SessionStream::SessionStreamError { - public: - enum Type {BadRequest, HostGone, HostUnknown, ImproperAddressing, - InternalServerError, ItemNotFound, OtherRequest, PolicyViolation, - RemoteConnectionFailed, RemoteStreamError, SeeOtherURI, SystemShutdown, UndefinedCondition, - NoError}; - BOSHError(Type type) : SessionStream::SessionStreamError(SessionStream::SessionStreamError::ConnectionReadError), type(type) {} - Type getType() {return type;} - typedef boost::shared_ptr<BOSHError> ref; - private: - Type type; - - }; + class SWIFTEN_API BOSHError : public SessionStream::SessionStreamError { + public: + enum Type { + BadRequest, HostGone, HostUnknown, ImproperAddressing, + InternalServerError, ItemNotFound, OtherRequest, PolicyViolation, + RemoteConnectionFailed, RemoteStreamError, SeeOtherURI, SystemShutdown, UndefinedCondition, + NoError}; + BOSHError(Type type) : SessionStream::SessionStreamError(SessionStream::SessionStreamError::ConnectionReadError), type(type) {} + Type getType() {return type;} + typedef boost::shared_ptr<BOSHError> ref; + + private: + Type type; + }; class SWIFTEN_API BOSHConnection : public boost::enable_shared_from_this<BOSHConnection> { public: typedef boost::shared_ptr<BOSHConnection> ref; - static ref create(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory) { - return ref(new BOSHConnection(boshURL, connector, parserFactory)); + static ref create(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory, TLSContextFactory* tlsContextFactory, const TLSOptions& tlsOptions) { + return ref(new BOSHConnection(boshURL, connector, parserFactory, tlsContextFactory, tlsOptions)); } virtual ~BOSHConnection(); virtual void connect(); @@ -71,6 +76,10 @@ namespace Swift { bool isReadyToSend(); void restartStream(); + bool setClientCertificate(CertificateWithKey::ref cert); + Certificate::ref getPeerCertificate() const; + std::vector<Certificate::ref> getPeerCertificateChain() const; + CertificateVerificationError::ref getPeerCertificateVerificationError() const; boost::signal<void (bool /* error */)> onConnectFinished; boost::signal<void (bool /* error */)> onDisconnected; @@ -84,7 +93,7 @@ namespace Swift { private: friend class ::BOSHConnectionTest; - BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory); + BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory, TLSContextFactory* tlsContextFactory, const TLSOptions& tlsOptions); static std::pair<SafeByteArray, size_t> createHTTPRequest(const SafeByteArray& data, bool streamRestart, bool terminate, unsigned long long rid, const std::string& sid, const URL& boshURL); void handleConnectFinished(Connection::ref); @@ -94,10 +103,19 @@ namespace Swift { BOSHError::Type parseTerminationCondition(const std::string& text); void cancelConnector(); + void handleTLSConnected(); + void handleTLSApplicationDataRead(const SafeByteArray& data); + void handleTLSNetowrkDataWriteRequest(const SafeByteArray& data); + void handleRawDataRead(boost::shared_ptr<SafeByteArray> data); + void handleTLSError(boost::shared_ptr<TLSError> error); + void writeData(const SafeByteArray& data); + URL boshURL_; Connector::ref connector_; XMLParserFactory* parserFactory_; boost::shared_ptr<Connection> connection_; + boost::shared_ptr<HighLayer> dummyLayer_; + boost::shared_ptr<TLSLayer> tlsLayer_; std::string sid_; bool waitingForStartResponse_; unsigned long long rid_; diff --git a/Swiften/Network/BOSHConnectionPool.cpp b/Swiften/Network/BOSHConnectionPool.cpp index c23e2de..57c1bcc 100644 --- a/Swiften/Network/BOSHConnectionPool.cpp +++ b/Swiften/Network/BOSHConnectionPool.cpp @@ -10,11 +10,11 @@ #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> +#include <Swiften/Base/Log.h> #include <Swiften/Base/SafeString.h> #include <Swiften/Base/foreach.h> #include <Swiften/Network/CachingDomainNameResolver.h> #include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h> -#include <Swiften/Network/TLSConnectionFactory.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, const TLSOptions& tlsOptions, boost::shared_ptr<HTTPTrafficFilter> trafficFilter) : @@ -27,21 +27,14 @@ BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* r to(to), requestLimit(2), restartCount(0), - pendingRestart(false) { + pendingRestart(false), + tlsContextFactory_(tlsFactory), + tlsOptions_(tlsOptions) { if (!boshHTTPConnectProxyURL.isEmpty()) { - if (boshHTTPConnectProxyURL.getScheme() == "https") { - connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory, tlsOptions); - myConnectionFactories.push_back(connectionFactory); - } connectionFactory = new HTTPConnectProxiedConnectionFactory(realResolver, connectionFactory, timerFactory, boshHTTPConnectProxyURL.getHost(), URL::getPortOrDefaultPort(boshHTTPConnectProxyURL), boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword, trafficFilter); } - if (boshURL.getScheme() == "https") { - connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory, tlsOptions); - myConnectionFactories.push_back(connectionFactory); - } resolver = new CachingDomainNameResolver(realResolver, eventLoop); - createConnection(); } BOSHConnectionPool::~BOSHConnectionPool() { @@ -83,11 +76,39 @@ void BOSHConnectionPool::restartStream() { } } +void BOSHConnectionPool::setTLSCertificate(CertificateWithKey::ref certWithKey) { + clientCertificate = certWithKey; +} + +bool BOSHConnectionPool::isTLSEncrypted() const { + return !pinnedCertificateChain_.empty(); +} + +Certificate::ref BOSHConnectionPool::getPeerCertificate() const { + Certificate::ref peerCertificate; + if (!pinnedCertificateChain_.empty()) { + peerCertificate = pinnedCertificateChain_[0]; + } + return peerCertificate; +} + +std::vector<Certificate::ref> BOSHConnectionPool::getPeerCertificateChain() const { + return pinnedCertificateChain_; +} + +boost::shared_ptr<CertificateVerificationError> BOSHConnectionPool::getPeerCertificateVerificationError() const { + return lastVerificationError_; +} + void BOSHConnectionPool::writeFooter() { pendingTerminate = true; tryToSendQueuedData(); } +void BOSHConnectionPool::open() { + createConnection(); +} + void BOSHConnectionPool::close() { if (!sid.empty()) { writeFooter(); @@ -117,6 +138,13 @@ void BOSHConnectionPool::handleConnectFinished(bool error, BOSHConnection::ref c */ } else { + if (connection->getPeerCertificate() && pinnedCertificateChain_.empty()) { + pinnedCertificateChain_ = connection->getPeerCertificateChain(); + } + if (!pinnedCertificateChain_.empty()) { + lastVerificationError_ = connection->getPeerCertificateVerificationError(); + } + if (sid.empty()) { connection->startStream(to, rid); } @@ -226,7 +254,7 @@ void BOSHConnectionPool::handleConnectionDisconnected(bool/* error*/, BOSHConnec boost::shared_ptr<BOSHConnection> BOSHConnectionPool::createConnection() { Connector::ref connector = Connector::create(boshURL.getHost(), URL::getPortOrDefaultPort(boshURL), boost::optional<std::string>(), resolver, connectionFactory, timerFactory); - BOSHConnection::ref connection = BOSHConnection::create(boshURL, connector, xmlParserFactory); + BOSHConnection::ref connection = BOSHConnection::create(boshURL, connector, xmlParserFactory, tlsContextFactory_, tlsOptions_); connection->onXMPPDataRead.connect(boost::bind(&BOSHConnectionPool::handleDataRead, this, _1)); connection->onSessionStarted.connect(boost::bind(&BOSHConnectionPool::handleSessionStarted, this, _1, _2)); connection->onBOSHDataRead.connect(boost::bind(&BOSHConnectionPool::handleBOSHDataRead, this, _1)); @@ -235,6 +263,12 @@ boost::shared_ptr<BOSHConnection> BOSHConnectionPool::createConnection() { connection->onConnectFinished.connect(boost::bind(&BOSHConnectionPool::handleConnectFinished, this, _1, connection)); connection->onSessionTerminated.connect(boost::bind(&BOSHConnectionPool::handleSessionTerminated, this, _1)); connection->onHTTPError.connect(boost::bind(&BOSHConnectionPool::handleHTTPError, this, _1)); + + if (boshURL.getScheme() == "https") { + bool success = connection->setClientCertificate(clientCertificate); + SWIFT_LOG(debug) << "setClientCertificate, success: " << success << std::endl; + } + connection->connect(); connections.push_back(connection); return connection; diff --git a/Swiften/Network/BOSHConnectionPool.h b/Swiften/Network/BOSHConnectionPool.h index eaef56c..df116ff 100644 --- a/Swiften/Network/BOSHConnectionPool.h +++ b/Swiften/Network/BOSHConnectionPool.h @@ -12,6 +12,7 @@ #include <Swiften/Base/API.h> #include <Swiften/Base/SafeString.h> #include <Swiften/Network/BOSHConnection.h> +#include <Swiften/TLS/CertificateWithKey.h> #include <Swiften/TLS/TLSOptions.h> namespace Swift { @@ -19,17 +20,27 @@ namespace Swift { class EventLoop; class HTTPConnectProxiedConnectionFactory; class HTTPTrafficFilter; - class TLSConnectionFactory; + class TLSContextFactory; + 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, const TLSOptions& tlsOptions, boost::shared_ptr<HTTPTrafficFilter> trafficFilter = boost::shared_ptr<HTTPTrafficFilter>()); ~BOSHConnectionPool(); + + void open(); void write(const SafeByteArray& data); void writeFooter(); void close(); void restartStream(); + void setTLSCertificate(CertificateWithKey::ref certWithKey); + bool isTLSEncrypted() const; + Certificate::ref getPeerCertificate() const; + std::vector<Certificate::ref> getPeerCertificateChain() const; + boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; + boost::signal<void (BOSHError::ref)> onSessionTerminated; boost::signal<void ()> onSessionStarted; boost::signal<void (const SafeByteArray&)> onXMPPDataRead; @@ -68,5 +79,10 @@ namespace Swift { bool pendingRestart; std::vector<ConnectionFactory*> myConnectionFactories; CachingDomainNameResolver* resolver; + CertificateWithKey::ref clientCertificate; + TLSContextFactory* tlsContextFactory_; + TLSOptions tlsOptions_; + std::vector<boost::shared_ptr<Certificate> > pinnedCertificateChain_; + boost::shared_ptr<CertificateVerificationError> lastVerificationError_; }; } diff --git a/Swiften/Network/BoostConnection.h b/Swiften/Network/BoostConnection.h index 83a8f01..8e89263 100644 --- a/Swiften/Network/BoostConnection.h +++ b/Swiften/Network/BoostConnection.h @@ -15,6 +15,9 @@ #include <Swiften/Base/SafeByteArray.h> #include <Swiften/EventLoop/EventOwner.h> #include <Swiften/Network/Connection.h> +#include <Swiften/TLS/Certificate.h> +#include <Swiften/TLS/CertificateVerificationError.h> +#include <Swiften/TLS/CertificateWithKey.h> namespace boost { class thread; @@ -47,6 +50,12 @@ namespace Swift { HostAddressPort getLocalAddress() const; + bool setClientCertificate(CertificateWithKey::ref cert); + + Certificate::ref getPeerCertificate() const; + std::vector<Certificate::ref> getPeerCertificateChain() const; + boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; + private: BoostConnection(boost::shared_ptr<boost::asio::io_service> ioService, EventLoop* eventLoop); diff --git a/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp b/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp index e687517..83ad548 100644 --- a/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp +++ b/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp @@ -1,20 +1,20 @@ /* - * Copyright (c) 2011 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/optional.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared.hpp> + #include <QA/Checker/IO.h> #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <boost/optional.hpp> -#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/EventLoop/DummyEventLoop.h> #include <Swiften/Network/BOSHConnection.h> @@ -27,8 +27,6 @@ #include <Swiften/Parser/PlatformXMLParserFactory.h> #include <Swiften/TLS/TLSOptions.h> - - using namespace Swift; typedef boost::shared_ptr<BOSHConnectionPool> PoolRef; @@ -323,10 +321,9 @@ 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(""), 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("")); + // make_shared is limited to 9 arguments; instead new is used here. + PoolRef pool = PoolRef(new BOSHConnectionPool(boshURL, resolver, connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString(""), TLSOptions())); + pool->open(); 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/Network/UnitTest/BOSHConnectionTest.cpp b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp index c8bf1f0..349c282 100644 --- a/Swiften/Network/UnitTest/BOSHConnectionTest.cpp +++ b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp @@ -24,6 +24,7 @@ #include <Swiften/Network/HostAddressPort.h> #include <Swiften/Network/StaticDomainNameResolver.h> #include <Swiften/Parser/PlatformXMLParserFactory.h> +#include <Swiften/TLS/TLSOptions.h> using namespace Swift; @@ -47,6 +48,7 @@ class BOSHConnectionTest : public CppUnit::TestFixture { connectionFactory = new MockConnectionFactory(eventLoop); resolver = new StaticDomainNameResolver(eventLoop); timerFactory = new DummyTimerFactory(); + tlsContextFactory = NULL; connectFinished = false; disconnected = false; disconnectedError = false; @@ -193,7 +195,7 @@ class BOSHConnectionTest : public CppUnit::TestFixture { BOSHConnection::ref createTestling() { resolver->addAddress("wonderland.lit", HostAddress("127.0.0.1")); Connector::ref connector = Connector::create("wonderland.lit", 5280, boost::optional<std::string>(), resolver, connectionFactory, timerFactory); - BOSHConnection::ref c = BOSHConnection::create(URL("http", "wonderland.lit", 5280, "/http-bind"), connector, &parserFactory); + BOSHConnection::ref c = BOSHConnection::create(URL("http", "wonderland.lit", 5280, "/http-bind"), connector, &parserFactory, tlsContextFactory, TLSOptions()); c->onConnectFinished.connect(boost::bind(&BOSHConnectionTest::handleConnectFinished, this, _1)); c->onDisconnected.connect(boost::bind(&BOSHConnectionTest::handleDisconnected, this, _1)); c->onXMPPDataRead.connect(boost::bind(&BOSHConnectionTest::handleDataRead, this, _1)); @@ -294,6 +296,7 @@ class BOSHConnectionTest : public CppUnit::TestFixture { PlatformXMLParserFactory parserFactory; StaticDomainNameResolver* resolver; TimerFactory* timerFactory; + TLSContextFactory* tlsContextFactory; std::string sid; }; diff --git a/Swiften/QA/TLSTest/CertificateErrorTest.cpp b/Swiften/QA/TLSTest/CertificateErrorTest.cpp index d7c2c55..e69af0b 100644 --- a/Swiften/QA/TLSTest/CertificateErrorTest.cpp +++ b/Swiften/QA/TLSTest/CertificateErrorTest.cpp @@ -48,7 +48,6 @@ class CertificateErrorTest : public CppUnit::TestFixture { public: void setUp() { - SWIFT_LOG(debug) << std::endl << std::endl; eventLoop_ = new DummyEventLoop(); boostIOServiceThread_ = new BoostIOServiceThread(); boostIOService_ = boost::make_shared<boost::asio::io_service>(); @@ -96,7 +95,6 @@ class CertificateErrorTest : public CppUnit::TestFixture { } void connectToServer(boost::shared_ptr<TLSConnection> connection, const std::string& hostname, int port) { - Log::setLogLevel(Log::debug); connection->onConnectFinished.connect(boost::bind(&CertificateErrorTest::handleConnectFinished, this, _1)); HostAddress address = resolveName(hostname); @@ -141,7 +139,6 @@ class CertificateErrorTest : public CppUnit::TestFixture { } void testTLS_O_MaticCertificateFromUnknownCA() { - Log::setLogLevel(Log::debug); boost::shared_ptr<TLSConnection> connection = boost::dynamic_pointer_cast<TLSConnection>(tlsConnectionFactory_->createConnection()); TLSContext* context = connection->getTLSContext(); diff --git a/Swiften/Session/BOSHSessionStream.cpp b/Swiften/Session/BOSHSessionStream.cpp index 99af71d..7fb8b31 100644 --- a/Swiften/Session/BOSHSessionStream.cpp +++ b/Swiften/Session/BOSHSessionStream.cpp @@ -83,6 +83,11 @@ BOSHSessionStream::~BOSHSessionStream() { xmppLayer = NULL; } +void BOSHSessionStream::open() { + connectionPool->setTLSCertificate(getTLSCertificate()); + connectionPool->open(); +} + void BOSHSessionStream::handlePoolXMPPDataRead(const SafeByteArray& data) { xmppLayer->handleDataRead(data); } @@ -118,19 +123,19 @@ void BOSHSessionStream::addTLSEncryption() { } bool BOSHSessionStream::isTLSEncrypted() { - return false; + return connectionPool->isTLSEncrypted(); } Certificate::ref BOSHSessionStream::getPeerCertificate() const { - return Certificate::ref(); + return connectionPool->getPeerCertificate(); } std::vector<Certificate::ref> BOSHSessionStream::getPeerCertificateChain() const { - return std::vector<Certificate::ref>(); + return connectionPool->getPeerCertificateChain(); } boost::shared_ptr<CertificateVerificationError> BOSHSessionStream::getPeerCertificateVerificationError() const { - return boost::shared_ptr<CertificateVerificationError>(); + return connectionPool->getPeerCertificateVerificationError(); } ByteArray BOSHSessionStream::getTLSFinishMessage() const { diff --git a/Swiften/Session/BOSHSessionStream.h b/Swiften/Session/BOSHSessionStream.h index 191a6f4..66e41ea 100644 --- a/Swiften/Session/BOSHSessionStream.h +++ b/Swiften/Session/BOSHSessionStream.h @@ -51,6 +51,7 @@ namespace Swift { ); virtual ~BOSHSessionStream(); + void open(); virtual void close(); virtual bool isOpen(); diff --git a/Swiften/StreamStack/DummyStreamLayer.h b/Swiften/StreamStack/DummyStreamLayer.h new file mode 100644 index 0000000..b06c8aa --- /dev/null +++ b/Swiften/StreamStack/DummyStreamLayer.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Base/API.h> +#include <Swiften/StreamStack/HighLayer.h> +#include <Swiften/StreamStack/LowLayer.h> + +namespace Swift { + /** + * The \ref DummyStreamLayer can be used to use a \ref LowLayer on its own, without a functioning parent layer. + * The \ref DummyStreamLayer will serve as the parent layer to the \ref LowLayer and is called when the \ref LowLayer + * wants to write data to its parent layer. + */ + class SWIFTEN_API DummyStreamLayer : public HighLayer { + public: + DummyStreamLayer(LowLayer* lowLayer) { + setChildLayer(lowLayer); + lowLayer->setParentLayer(this); + } + + virtual void handleDataRead(const SafeByteArray& /* data */) { + + } + }; +} diff --git a/Swiften/StreamStack/LowLayer.h b/Swiften/StreamStack/LowLayer.h index 7d7e0b5..b1bddec 100644 --- a/Swiften/StreamStack/LowLayer.h +++ b/Swiften/StreamStack/LowLayer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -14,6 +14,7 @@ namespace Swift { class SWIFTEN_API LowLayer { friend class StreamStack; + friend class DummyStreamLayer; public: LowLayer(); |