summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2015-10-19 14:23:42 (GMT)
committerSwift Review <review@swift.im>2015-11-10 18:28:39 (GMT)
commitab651834c9088e34e6e66eb105e0fb855f1572c4 (patch)
tree611d434fedcb69f6698af9dc4696e59fba9d97b7
parent9b090062d0270d556c9253e0ddf767d7c5bbadbf (diff)
downloadswift-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.cpp39
-rw-r--r--Swiften/Network/BOSHConnection.cpp104
-rw-r--r--Swiften/Network/BOSHConnection.h58
-rw-r--r--Swiften/Network/BOSHConnectionPool.cpp58
-rw-r--r--Swiften/Network/BOSHConnectionPool.h18
-rw-r--r--Swiften/Network/BoostConnection.h9
-rw-r--r--Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp23
-rw-r--r--Swiften/Network/UnitTest/BOSHConnectionTest.cpp5
-rw-r--r--Swiften/QA/TLSTest/CertificateErrorTest.cpp3
-rw-r--r--Swiften/Session/BOSHSessionStream.cpp13
-rw-r--r--Swiften/Session/BOSHSessionStream.h1
-rw-r--r--Swiften/StreamStack/DummyStreamLayer.h30
-rw-r--r--Swiften/StreamStack/LowLayer.h3
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
@@ -121,31 +121,36 @@ void CoreClient::connect(const ClientOptions& o) {
connector_->start();
}
else {
/* 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();
}
}
void CoreClient::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
@@ -21,25 +21,35 @@
#include <Swiften/Base/ByteArray.h>
#include <Swiften/Base/Concat.h>
#include <Swiften/Base/Log.h>
#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),
sid_(),
waitingForStartResponse_(false),
rid_(~0ULL),
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() {
cancelConnector();
if (connection_) {
connection_->onDataRead.disconnect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1));
@@ -58,12 +68,45 @@ void BOSHConnection::cancelConnector() {
connector_->onConnectFinished.disconnect(boost::bind(&BOSHConnection::handleConnectFinished, shared_from_this(), _1));
connector_->stop();
connector_.reset();
}
}
+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();
sid_ = "";
}
else {
@@ -73,12 +116,46 @@ void BOSHConnection::disconnect() {
}
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);
}
void BOSHConnection::write(const SafeByteArray& data) {
@@ -126,27 +203,42 @@ void BOSHConnection::write(const SafeByteArray& data, bool streamRestart, bool t
assert(connectionReady_);
assert(!sid_.empty());
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;
}
void BOSHConnection::handleConnectFinished(Connection::ref connection) {
cancelConnector();
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) {
assert(connectionReady_);
// Session Creation Request
std::stringstream content;
@@ -177,13 +269,13 @@ void BOSHConnection::startStream(const std::string& to, unsigned long long rid)
<< "Content-Length: " << contentString.size() << "\r\n\r\n"
<< contentString;
waitingForStartResponse_ = true;
SafeByteArray safeHeader = createSafeByteArray(header.str());
onBOSHDataWritten(safeHeader);
- connection_->write(safeHeader);
+ writeData(safeHeader);
SWIFT_LOG(debug) << "write stream header: " << safeByteArrayToString(safeHeader) << std::endl;
}
void BOSHConnection::handleDataRead(boost::shared_ptr<SafeByteArray> data) {
onBOSHDataRead(*data);
buffer_ = concat(buffer_, *data);
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
@@ -2,30 +2,31 @@
* Copyright (c) 2011 Thilo Cestonaro
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
/*
- * Copyright (c) 2011 Isode Limited.
+ * Copyright (c) 2011-2015 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#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;
namespace system {
class error_code;
}
@@ -33,33 +34,37 @@ namespace boost {
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();
virtual void disconnect();
virtual void write(const SafeByteArray& data);
@@ -68,12 +73,16 @@ namespace Swift {
void setSID(const std::string& sid);
void startStream(const std::string& to, unsigned long long rid);
void terminateStream();
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;
boost::signal<void (BOSHError::ref)> onSessionTerminated;
boost::signal<void (const std::string& /*sid*/, size_t /*requests*/)> onSessionStarted;
boost::signal<void (const SafeByteArray&)> onXMPPDataRead;
@@ -81,26 +90,35 @@ namespace Swift {
boost::signal<void (const SafeByteArray&)> onBOSHDataWritten;
boost::signal<void (const std::string&)> onHTTPError;
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);
void handleDataRead(boost::shared_ptr<SafeByteArray> data);
void handleDisconnected(const boost::optional<Connection::Error>& error);
void write(const SafeByteArray& data, bool streamRestart, bool terminate); /* FIXME: refactor */
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_;
SafeByteArray buffer_;
bool pending_;
bool connectionReady_;
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
@@ -7,44 +7,37 @@
#include <climits>
#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) :
boshURL(boshURL),
connectionFactory(connectionFactoryParameter),
xmlParserFactory(parserFactory),
timerFactory(timerFactory),
rid(initialRID),
pendingTerminate(false),
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() {
/* Don't do a normal close here. Instead kill things forcibly, as close() or writeFooter() will already have been called */
std::vector<BOSHConnection::ref> connectionCopies = connections;
foreach (BOSHConnection::ref connection, connectionCopies) {
@@ -80,17 +73,45 @@ void BOSHConnectionPool::restartStream() {
}
else {
pendingRestart = true;
}
}
+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();
}
else {
pendingTerminate = true;
@@ -114,12 +135,19 @@ void BOSHConnectionPool::handleConnectFinished(bool error, BOSHConnection::ref c
onSessionTerminated(boost::make_shared<BOSHError>(BOSHError::UndefinedCondition));
/*TODO: We can probably manage to not terminate the stream here and use the rid/ack retry
* logic to just swallow the error and try again (some number of tries).
*/
}
else {
+ if (connection->getPeerCertificate() && pinnedCertificateChain_.empty()) {
+ pinnedCertificateChain_ = connection->getPeerCertificateChain();
+ }
+ if (!pinnedCertificateChain_.empty()) {
+ lastVerificationError_ = connection->getPeerCertificateVerificationError();
+ }
+
if (sid.empty()) {
connection->startStream(to, rid);
}
if (pendingRestart) {
restartStream();
}
@@ -223,21 +251,27 @@ void BOSHConnectionPool::handleConnectionDisconnected(bool/* error*/, BOSHConnec
tryToSendQueuedData();
}
}
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));
connection->onBOSHDataWritten.connect(boost::bind(&BOSHConnectionPool::handleBOSHDataWritten, this, _1));
connection->onDisconnected.connect(boost::bind(&BOSHConnectionPool::handleConnectionDisconnected, this, _1, connection));
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;
}
void BOSHConnectionPool::destroyConnection(boost::shared_ptr<BOSHConnection> 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
@@ -9,30 +9,41 @@
#include <vector>
#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 {
class CachingDomainNameResolver;
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;
boost::signal<void (const SafeByteArray&)> onBOSHDataRead;
boost::signal<void (const SafeByteArray&)> onBOSHDataWritten;
@@ -65,8 +76,13 @@ namespace Swift {
std::string to;
size_t requestLimit;
int restartCount;
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
@@ -12,12 +12,15 @@
#include <boost/thread/mutex.hpp>
#include <Swiften/Base/API.h>
#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;
namespace system {
class error_code;
}
@@ -44,12 +47,18 @@ namespace Swift {
boost::asio::ip::tcp::socket& getSocket() {
return socket_;
}
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);
void handleConnectFinished(const boost::system::error_code& error);
void handleSocketRead(const boost::system::error_code& error, size_t bytesTransferred);
void handleDataWritten(const boost::system::error_code& error);
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,37 +1,35 @@
/*
- * 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>
#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/Parser/PlatformXMLParserFactory.h>
#include <Swiften/TLS/TLSOptions.h>
-
-
using namespace Swift;
typedef boost::shared_ptr<BOSHConnectionPool> PoolRef;
class BOSHConnectionPoolTest : public CppUnit::TestFixture {
@@ -320,16 +318,15 @@ 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));
pool->onSessionStarted.connect(boost::bind(&BOSHConnectionPoolTest::handleSessionStarted, this));
pool->onSessionTerminated.connect(boost::bind(&BOSHConnectionPoolTest::handleSessionTerminated, this));
eventLoop->processEvents();
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
@@ -21,12 +21,13 @@
#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/Parser/PlatformXMLParserFactory.h>
+#include <Swiften/TLS/TLSOptions.h>
using namespace Swift;
class BOSHConnectionTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(BOSHConnectionTest);
CPPUNIT_TEST(testHeader);
@@ -44,12 +45,13 @@ class BOSHConnectionTest : public CppUnit::TestFixture {
public:
void setUp() {
eventLoop = new DummyEventLoop();
connectionFactory = new MockConnectionFactory(eventLoop);
resolver = new StaticDomainNameResolver(eventLoop);
timerFactory = new DummyTimerFactory();
+ tlsContextFactory = NULL;
connectFinished = false;
disconnected = false;
disconnectedError = false;
dataRead.clear();
}
@@ -190,13 +192,13 @@ class BOSHConnectionTest : public CppUnit::TestFixture {
private:
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));
c->onSessionStarted.connect(boost::bind(&BOSHConnectionTest::handleSID, this, _1));
c->setRID(42);
return c;
@@ -291,12 +293,13 @@ class BOSHConnectionTest : public CppUnit::TestFixture {
bool disconnected;
bool disconnectedError;
ByteArray dataRead;
PlatformXMLParserFactory parserFactory;
StaticDomainNameResolver* resolver;
TimerFactory* timerFactory;
+ TLSContextFactory* tlsContextFactory;
std::string sid;
};
CPPUNIT_TEST_SUITE_REGISTRATION(BOSHConnectionTest);
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
@@ -45,13 +45,12 @@ class CertificateErrorTest : public CppUnit::TestFixture {
#endif
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
- SWIFT_LOG(debug) << std::endl << std::endl;
eventLoop_ = new DummyEventLoop();
boostIOServiceThread_ = new BoostIOServiceThread();
boostIOService_ = boost::make_shared<boost::asio::io_service>();
connectionFactory_ = new BoostConnectionFactory(boostIOServiceThread_->getIOService(), eventLoop_);
idnConverter_ = PlatformIDNConverter::create();
domainNameResolver_ = new PlatformDomainNameResolver(idnConverter_, eventLoop_),
@@ -93,13 +92,12 @@ class CertificateErrorTest : public CppUnit::TestFixture {
}
return lastResoverResult_;
}
void connectToServer(boost::shared_ptr<TLSConnection> connection, const std::string& hostname, int port) {
- Log::setLogLevel(Log::debug);
connection->onConnectFinished.connect(boost::bind(&CertificateErrorTest::handleConnectFinished, this, _1));
HostAddress address = resolveName(hostname);
connection->connect(HostAddressPort(address, port));
@@ -138,13 +136,12 @@ class CertificateErrorTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(true, connectFinishedWithError_);
CPPUNIT_ASSERT(context->getPeerCertificateVerificationError());
CPPUNIT_ASSERT_EQUAL(CertificateVerificationError::Expired, context->getPeerCertificateVerificationError()->getType());
}
void testTLS_O_MaticCertificateFromUnknownCA() {
- Log::setLogLevel(Log::debug);
boost::shared_ptr<TLSConnection> connection = boost::dynamic_pointer_cast<TLSConnection>(tlsConnectionFactory_->createConnection());
TLSContext* context = connection->getTLSContext();
connectToServer(connection, "test7.tls-o-matic.com", 407);
CPPUNIT_ASSERT_EQUAL(true, connectFinishedWithError_);
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
@@ -80,12 +80,17 @@ BOSHSessionStream::~BOSHSessionStream() {
xmppLayer->onError.disconnect(boost::bind(&BOSHSessionStream::handleXMPPError, this));
xmppLayer->onWriteData.disconnect(boost::bind(&BOSHSessionStream::handleXMPPLayerDataWritten, this, _1));
delete xmppLayer;
xmppLayer = NULL;
}
+void BOSHSessionStream::open() {
+ connectionPool->setTLSCertificate(getTLSCertificate());
+ connectionPool->open();
+}
+
void BOSHSessionStream::handlePoolXMPPDataRead(const SafeByteArray& data) {
xmppLayer->handleDataRead(data);
}
void BOSHSessionStream::writeElement(boost::shared_ptr<ToplevelElement> element) {
assert(available);
@@ -115,25 +120,25 @@ bool BOSHSessionStream::supportsTLSEncryption() {
void BOSHSessionStream::addTLSEncryption() {
assert(available);
}
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 {
return ByteArray();
}
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
@@ -48,12 +48,13 @@ namespace Swift {
const SafeString& boshHTTPConnectProxyAuthPassword,
const TLSOptions& tlsOptions,
boost::shared_ptr<HTTPTrafficFilter> trafficFilter
);
virtual ~BOSHSessionStream();
+ void open();
virtual void close();
virtual bool isOpen();
virtual void writeHeader(const ProtocolHeader& header);
virtual void writeElement(boost::shared_ptr<ToplevelElement>);
virtual void writeFooter();
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,8 +1,8 @@
/*
- * Copyright (c) 2010 Isode Limited.
+ * Copyright (c) 2010-2015 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
@@ -11,12 +11,13 @@
namespace Swift {
class HighLayer;
class SWIFTEN_API LowLayer {
friend class StreamStack;
+ friend class DummyStreamLayer;
public:
LowLayer();
virtual ~LowLayer();
virtual void writeData(const SafeByteArray& data) = 0;