summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2018-02-02 06:44:28 (GMT)
committerTobias Markmann <tm@ayena.de>2018-02-02 12:48:17 (GMT)
commit9919bcfde2a166f815d259712af516a2dc3bd896 (patch)
treeeb0fc07368c86e4fe5c10cb2284f1284c6a0641f /Swiften/TLS/OpenSSL
parent9e2eee27d47ff1523677eb3881b4edcf66d7c0db (diff)
downloadswift-9919bcfde2a166f815d259712af516a2dc3bd896.zip
swift-9919bcfde2a166f815d259712af516a2dc3bd896.tar.bz2
Add basic TLS server support in OpenTLSContext
This also extends the TLSContext interface with methods required for server mode. Test-Information: Added unit tests that test new functionality in TLSContex. This includes test certificates in the source file that are not for public use. This new ClientServerTest is only enabled for OpenSSL, as other TLS backends do not support the new functionality yet. Tested on macOS 10.13.3 with clang-trunk. Change-Id: I8e43476057608067eb3b9852328aa21cd22974a0
Diffstat (limited to 'Swiften/TLS/OpenSSL')
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.cpp182
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.h11
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp6
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContextFactory.h12
4 files changed, 192 insertions, 19 deletions
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
index 6f15edf..f90b4a8 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
@@ -21,6 +21,8 @@
#include <Security/Security.h>
#endif
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/Algorithm.h>
#include <Swiften/TLS/OpenSSL/OpenSSLContext.h>
#include <Swiften/TLS/OpenSSL/OpenSSLCertificate.h>
#include <Swiften/TLS/CertificateWithKey.h>
@@ -61,11 +63,33 @@ namespace {
OpenSSLInitializerFinalizer(const OpenSSLInitializerFinalizer &) = delete;
};
-}
-OpenSSLContext::OpenSSLContext() : state_(State::Start) {
+ std::unique_ptr<SSL_CTX> createSSL_CTX(OpenSSLContext::Mode mode) {
+ std::unique_ptr<SSL_CTX> sslCtx;
+ switch (mode) {
+ case OpenSSLContext::Mode::Client:
+ sslCtx = std::unique_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_client_method()));
+ break;
+ case OpenSSLContext::Mode::Server:
+ sslCtx = std::unique_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_server_method()));
+ break;
+ }
+ return sslCtx;
+ }
+
+ std::string openSSLInternalErrorToString() {
+ auto bio = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free);
+ ERR_print_errors(bio.get());
+ std::string errorString;
+ errorString.resize(BIO_pending(bio.get()));
+ BIO_read(bio.get(), (void*)errorString.data(), errorString.size());
+ return errorString;
+ }
+ }
+
+OpenSSLContext::OpenSSLContext(Mode mode) : mode_(mode), state_(State::Start) {
ensureLibraryInitialized();
- context_ = std::unique_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_client_method()));
+ context_ = createSSL_CTX(mode_);
SSL_CTX_set_options(context_.get(), SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
// TODO: implement CRL checking
@@ -129,7 +153,30 @@ void OpenSSLContext::ensureLibraryInitialized() {
static OpenSSLInitializerFinalizer openSSLInit;
}
+void OpenSSLContext::initAndSetBIOs() {
+ // Ownership of BIOs is transferred
+ readBIO_ = BIO_new(BIO_s_mem());
+ writeBIO_ = BIO_new(BIO_s_mem());
+ SSL_set_bio(handle_.get(), readBIO_, writeBIO_);
+}
+
+void OpenSSLContext::accept() {
+ assert(mode_ == Mode::Server);
+ handle_ = std::unique_ptr<SSL>(SSL_new(context_.get()));
+ if (!handle_) {
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>());
+ return;
+ }
+
+ initAndSetBIOs();
+
+ state_ = State::Accepting;
+ doAccept();
+}
+
void OpenSSLContext::connect() {
+ assert(mode_ == Mode::Client);
handle_ = std::unique_ptr<SSL>(SSL_new(context_.get()));
if (!handle_) {
state_ = State::Error;
@@ -137,15 +184,40 @@ void OpenSSLContext::connect() {
return;
}
- // Ownership of BIOs is transferred
- readBIO_ = BIO_new(BIO_s_mem());
- writeBIO_ = BIO_new(BIO_s_mem());
- SSL_set_bio(handle_.get(), readBIO_, writeBIO_);
+ // Ownership of BIOs is transferred to the SSL_CTX instance in handle_.
+ initAndSetBIOs();
state_ = State::Connecting;
doConnect();
}
+void OpenSSLContext::doAccept() {
+ auto acceptResult = SSL_accept(handle_.get());
+ auto error = SSL_get_error(handle_.get(), acceptResult);
+ switch (error) {
+ case SSL_ERROR_NONE: {
+ state_ = State::Connected;
+ //std::cout << x->name << std::endl;
+ //const char* comp = SSL_get_current_compression(handle_.get());
+ //std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl;
+ onConnected();
+ // The following call is important so the client knowns the handshake is finished.
+ sendPendingDataToNetwork();
+ break;
+ }
+ case SSL_ERROR_WANT_READ:
+ sendPendingDataToNetwork();
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ sendPendingDataToNetwork();
+ break;
+ default:
+ SWIFT_LOG(warning) << openSSLInternalErrorToString() << std::endl;
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>());
+ }
+}
+
void OpenSSLContext::doConnect() {
int connectResult = SSL_connect(handle_.get());
int error = SSL_get_error(handle_.get(), connectResult);
@@ -162,6 +234,7 @@ void OpenSSLContext::doConnect() {
sendPendingDataToNetwork();
break;
default:
+ SWIFT_LOG(warning) << openSSLInternalErrorToString() << std::endl;
state_ = State::Error;
onError(std::make_shared<TLSError>());
}
@@ -180,6 +253,9 @@ void OpenSSLContext::sendPendingDataToNetwork() {
void OpenSSLContext::handleDataFromNetwork(const SafeByteArray& data) {
BIO_write(readBIO_, vecptr(data), data.size());
switch (state_) {
+ case State::Accepting:
+ doAccept();
+ break;
case State::Connecting:
doConnect();
break;
@@ -217,6 +293,98 @@ void OpenSSLContext::sendPendingDataToApplication() {
}
}
+bool OpenSSLContext::setCertificateChain(const std::vector<Certificate::ref>& certificateChain) {
+ if (certificateChain.size() == 0) {
+ SWIFT_LOG(warning) << "Trying to load empty certificate chain." << std::endl;
+ return false;
+ }
+
+ // load endpoint certificate
+ auto openSSLCert = std::dynamic_pointer_cast<OpenSSLCertificate>(certificateChain[0]);
+ if (!openSSLCert) {
+ return false;
+ }
+
+ if (SSL_CTX_use_certificate(context_.get(), openSSLCert->getInternalX509().get()) != 1) {
+ return false;
+ }
+
+ if (certificateChain.size() > 1) {
+ for (auto certificate : range(certificateChain.begin() + 1, certificateChain.end())) {
+ auto openSSLCert = std::dynamic_pointer_cast<OpenSSLCertificate>(certificate);
+ if (!openSSLCert) {
+ return false;
+ }
+ if (SSL_CTX_add_extra_chain_cert(context_.get(), openSSLCert->getInternalX509().get()) != 1) {
+ SWIFT_LOG(warning) << "Trying to load empty certificate chain." << std::endl;
+ return false;
+ }
+ }
+ }
+
+ if (handle_) {
+ // This workaround is needed as OpenSSL has a shortcut to not do anything
+ // if you set the SSL_CTX to the existing SSL_CTX and not reloading the
+ // certificates from the SSL_CTX.
+ auto dummyContext = createSSL_CTX(mode_);
+ SSL_set_SSL_CTX(handle_.get(), dummyContext.get());
+ SSL_set_SSL_CTX(handle_.get(), context_.get());
+ }
+
+ return true;
+}
+
+int empty_or_preset_password_cb(char* buf, int max_len, int flag, void* password);
+
+int empty_or_preset_password_cb(char* buf, int max_len, int /* flag */, void* password) {
+ char* charPassword = (char*)password;
+ if (charPassword == nullptr) {
+ return 0;
+ }
+ int len = strlen(charPassword);
+ if(len > max_len) {
+ return 0;
+ }
+ memcpy(buf, charPassword, len);
+ return len;
+}
+
+bool OpenSSLContext::setPrivateKey(const PrivateKey::ref& privateKey) {
+ if (privateKey->getData().size() > std::numeric_limits<int>::max()) {
+ return false;
+ }
+
+ auto bio = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free);
+ BIO_write(bio.get(), vecptr(privateKey->getData()), int(privateKey->getData().size()));
+
+ SafeByteArray safePassword;
+ void* password = nullptr;
+ if (privateKey->getPassword()) {
+ safePassword = privateKey->getPassword().get();
+ safePassword.push_back(0);
+ password = safePassword.data();
+ }
+ auto resultKey = PEM_read_bio_PrivateKey(bio.get(), nullptr, empty_or_preset_password_cb, password);
+ if (resultKey) {
+ if (handle_) {
+ auto result = SSL_use_PrivateKey(handle_.get(), resultKey);;
+ if (result != 1) {
+ return false;
+ }
+ }
+ else {
+ auto result = SSL_CTX_use_PrivateKey(context_.get(), resultKey);
+ if (result != 1) {
+ return false;
+ }
+ }
+ }
+ else {
+ return false;
+ }
+ return true;
+}
+
bool OpenSSLContext::setClientCertificate(CertificateWithKey::ref certificate) {
std::shared_ptr<PKCS12Certificate> pkcs12Certificate = std::dynamic_pointer_cast<PKCS12Certificate>(certificate);
if (!pkcs12Certificate || pkcs12Certificate->isNull()) {
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h
index 49ada51..5f06811 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h
@@ -38,10 +38,14 @@ namespace std {
namespace Swift {
class OpenSSLContext : public TLSContext, boost::noncopyable {
public:
- OpenSSLContext();
+ OpenSSLContext(Mode mode);
virtual ~OpenSSLContext() override final;
+ void accept() override final;
void connect() override final;
+
+ bool setCertificateChain(const std::vector<Certificate::ref>& certificateChain) override final;
+ bool setPrivateKey(const PrivateKey::ref& privateKey) override final;
bool setClientCertificate(CertificateWithKey::ref cert) override final;
void handleDataFromNetwork(const SafeByteArray&) override final;
@@ -57,13 +61,16 @@ namespace Swift {
static CertificateVerificationError::Type getVerificationErrorTypeForResult(int);
+ void initAndSetBIOs();
+ void doAccept();
void doConnect();
void sendPendingDataToNetwork();
void sendPendingDataToApplication();
private:
- enum class State { Start, Connecting, Connected, Error };
+ enum class State { Start, Accepting, Connecting, Connected, Error };
+ Mode mode_;
State state_;
std::unique_ptr<SSL_CTX> context_;
std::unique_ptr<SSL> handle_;
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp
index 9f7b2aa..af0966e 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -15,8 +15,8 @@ bool OpenSSLContextFactory::canCreate() const {
return true;
}
-TLSContext* OpenSSLContextFactory::createTLSContext(const TLSOptions&) {
- return new OpenSSLContext();
+TLSContext* OpenSSLContextFactory::createTLSContext(const TLSOptions&, TLSContext::Mode mode) {
+ return new OpenSSLContext(mode);
}
void OpenSSLContextFactory::setCheckCertificateRevocation(bool check) {
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h
index e121a1a..63fc17e 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h
@@ -1,23 +1,21 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
-#include <cassert>
-
#include <Swiften/TLS/TLSContextFactory.h>
namespace Swift {
class OpenSSLContextFactory : public TLSContextFactory {
public:
- bool canCreate() const;
- virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions);
+ bool canCreate() const override final;
+ virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode) override final;
// Not supported
- virtual void setCheckCertificateRevocation(bool b);
- virtual void setDisconnectOnCardRemoval(bool b);
+ virtual void setCheckCertificateRevocation(bool b) override final;
+ virtual void setDisconnectOnCardRemoval(bool b) override final;
};
}