summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp4
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.cpp118
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.h49
3 files changed, 100 insertions, 71 deletions
diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp
index 17ac8cc..8d2d965 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp
@@ -3,73 +3,73 @@
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiften/TLS/OpenSSL/OpenSSLCertificate.h>
#include <Swiften/Base/ByteArray.h>
#include <Swiften/Base/Log.h>
#undef X509_NAME // Windows.h defines this, and for some reason, it doesn't get undeffed properly in x509.h
#include <openssl/x509v3.h>
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#pragma clang diagnostic ignored "-Wcast-align"
#pragma clang diagnostic ignored "-Wsign-conversion"
namespace Swift {
OpenSSLCertificate::OpenSSLCertificate(std::shared_ptr<X509> cert) : cert(cert) {
parse();
}
OpenSSLCertificate::OpenSSLCertificate(const ByteArray& der) {
#if OPENSSL_VERSION_NUMBER <= 0x009070cfL
unsigned char* p = const_cast<unsigned char*>(vecptr(der));
#else
const unsigned char* p = vecptr(der);
#endif
- cert = std::shared_ptr<X509>(d2i_X509(NULL, &p, der.size()), X509_free);
+ cert = std::shared_ptr<X509>(d2i_X509(nullptr, &p, der.size()), X509_free);
if (!cert) {
SWIFT_LOG(warning) << "Error creating certificate from DER data" << std::endl;
}
parse();
}
ByteArray OpenSSLCertificate::toDER() const {
ByteArray result;
if (!cert) {
return result;
}
- result.resize(i2d_X509(cert.get(), NULL));
+ result.resize(i2d_X509(cert.get(), nullptr));
unsigned char* p = vecptr(result);
i2d_X509(cert.get(), &p);
return result;
}
void OpenSSLCertificate::parse() {
if (!cert) {
return;
}
// Subject name
X509_NAME* subjectName = X509_get_subject_name(cert.get());
if (subjectName) {
// Subject name
ByteArray subjectNameData;
subjectNameData.resize(256);
X509_NAME_oneline(X509_get_subject_name(cert.get()), reinterpret_cast<char*>(vecptr(subjectNameData)), static_cast<unsigned int>(subjectNameData.size()));
this->subjectName = byteArrayToString(subjectNameData);
// Common name
int cnLoc = X509_NAME_get_index_by_NID(subjectName, NID_commonName, -1);
while (cnLoc != -1) {
X509_NAME_ENTRY* cnEntry = X509_NAME_get_entry(subjectName, cnLoc);
ASN1_STRING* cnData = X509_NAME_ENTRY_get_data(cnEntry);
commonNames.push_back(byteArrayToString(createByteArray(reinterpret_cast<const char*>(cnData->data), cnData->length)));
cnLoc = X509_NAME_get_index_by_NID(subjectName, NID_commonName, cnLoc);
}
}
// subjectAltNames
int subjectAltNameLoc = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1);
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
index 0805917..6f15edf 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
@@ -1,307 +1,317 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiften/Base/Platform.h>
#ifdef SWIFTEN_PLATFORM_WINDOWS
#include <windows.h>
#include <wincrypt.h>
#endif
+#include <cassert>
+#include <memory>
#include <vector>
+
#include <openssl/err.h>
#include <openssl/pkcs12.h>
-#include <memory>
#if defined(SWIFTEN_PLATFORM_MACOSX)
#include <Security/Security.h>
#endif
#include <Swiften/TLS/OpenSSL/OpenSSLContext.h>
#include <Swiften/TLS/OpenSSL/OpenSSLCertificate.h>
#include <Swiften/TLS/CertificateWithKey.h>
#include <Swiften/TLS/PKCS12Certificate.h>
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
#pragma clang diagnostic ignored "-Wcast-align"
#pragma clang diagnostic ignored "-Wsign-conversion"
namespace Swift {
static const int MAX_FINISHED_SIZE = 4096;
static const int SSL_READ_BUFFERSIZE = 8192;
static void freeX509Stack(STACK_OF(X509)* stack) {
sk_X509_free(stack);
}
-OpenSSLContext::OpenSSLContext() : state_(Start), context_(0), handle_(0), readBIO_(0), writeBIO_(0) {
+namespace {
+ class OpenSSLInitializerFinalizer {
+ public:
+ OpenSSLInitializerFinalizer() {
+ SSL_load_error_strings();
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+
+ // Disable compression
+ /*
+ STACK_OF(SSL_COMP)* compressionMethods = SSL_COMP_get_compression_methods();
+ sk_SSL_COMP_zero(compressionMethods);*/
+ }
+
+ ~OpenSSLInitializerFinalizer() {
+ EVP_cleanup();
+ }
+
+ OpenSSLInitializerFinalizer(const OpenSSLInitializerFinalizer &) = delete;
+ };
+}
+
+OpenSSLContext::OpenSSLContext() : state_(State::Start) {
ensureLibraryInitialized();
- context_ = SSL_CTX_new(SSLv23_client_method());
- SSL_CTX_set_options(context_, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+ context_ = std::unique_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_client_method()));
+ SSL_CTX_set_options(context_.get(), SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
// TODO: implement CRL checking
// TODO: download CRL (HTTP transport)
// TODO: cache CRL downloads for configurable time period
// TODO: implement OCSP support
// TODO: handle OCSP stapling see https://www.rfc-editor.org/rfc/rfc4366.txt
// Load system certs
#if defined(SWIFTEN_PLATFORM_WINDOWS)
- X509_STORE* store = SSL_CTX_get_cert_store(context_);
+ X509_STORE* store = SSL_CTX_get_cert_store(context_.get());
HCERTSTORE systemStore = CertOpenSystemStore(0, "ROOT");
if (systemStore) {
PCCERT_CONTEXT certContext = NULL;
while (true) {
certContext = CertFindCertificateInStore(systemStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, certContext);
if (!certContext) {
break;
}
OpenSSLCertificate cert(createByteArray(certContext->pbCertEncoded, certContext->cbCertEncoded));
if (store && cert.getInternalX509()) {
X509_STORE_add_cert(store, cert.getInternalX509().get());
}
}
}
#elif !defined(SWIFTEN_PLATFORM_MACOSX)
- SSL_CTX_set_default_verify_paths(context_);
+ SSL_CTX_set_default_verify_paths(context_.get());
#elif defined(SWIFTEN_PLATFORM_MACOSX) && !defined(SWIFTEN_PLATFORM_IPHONE)
// On Mac OS X 10.5 (OpenSSL < 0.9.8), OpenSSL does not automatically look in the system store.
// On Mac OS X 10.6 (OpenSSL >= 0.9.8), OpenSSL *does* look in the system store to determine trust.
// However, if there is a certificate error, it will always emit the "Invalid CA" error if we didn't add
// the certificates first. See
// http://opensource.apple.com/source/OpenSSL098/OpenSSL098-27/src/crypto/x509/x509_vfy_apple.c
// to understand why. We therefore add all certs from the system store ourselves.
- X509_STORE* store = SSL_CTX_get_cert_store(context_);
+ X509_STORE* store = SSL_CTX_get_cert_store(context_.get());
CFArrayRef anchorCertificates;
if (SecTrustCopyAnchorCertificates(&anchorCertificates) == 0) {
for (int i = 0; i < CFArrayGetCount(anchorCertificates); ++i) {
SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(const_cast<void*>(CFArrayGetValueAtIndex(anchorCertificates, i)));
CSSM_DATA certCSSMData;
if (SecCertificateGetData(cert, &certCSSMData) != 0 || certCSSMData.Length == 0) {
continue;
}
std::vector<unsigned char> certData;
certData.resize(certCSSMData.Length);
memcpy(&certData[0], certCSSMData.Data, certCSSMData.Length);
OpenSSLCertificate certificate(certData);
if (store && certificate.getInternalX509()) {
X509_STORE_add_cert(store, certificate.getInternalX509().get());
}
}
CFRelease(anchorCertificates);
}
#endif
}
OpenSSLContext::~OpenSSLContext() {
- SSL_free(handle_);
- SSL_CTX_free(context_);
}
void OpenSSLContext::ensureLibraryInitialized() {
- static bool isLibraryInitialized = false;
- if (!isLibraryInitialized) {
- SSL_load_error_strings();
- SSL_library_init();
- OpenSSL_add_all_algorithms();
-
- // Disable compression
- /*
- STACK_OF(SSL_COMP)* compressionMethods = SSL_COMP_get_compression_methods();
- sk_SSL_COMP_zero(compressionMethods);*/
-
- isLibraryInitialized = true;
- }
+ static OpenSSLInitializerFinalizer openSSLInit;
}
void OpenSSLContext::connect() {
- handle_ = SSL_new(context_);
- if (handle_ == nullptr) {
- state_ = Error;
+ handle_ = std::unique_ptr<SSL>(SSL_new(context_.get()));
+ if (!handle_) {
+ state_ = State::Error;
onError(std::make_shared<TLSError>());
return;
}
- // Ownership of BIOs is ransferred
+ // Ownership of BIOs is transferred
readBIO_ = BIO_new(BIO_s_mem());
writeBIO_ = BIO_new(BIO_s_mem());
- SSL_set_bio(handle_, readBIO_, writeBIO_);
+ SSL_set_bio(handle_.get(), readBIO_, writeBIO_);
- state_ = Connecting;
+ state_ = State::Connecting;
doConnect();
}
void OpenSSLContext::doConnect() {
- int connectResult = SSL_connect(handle_);
- int error = SSL_get_error(handle_, connectResult);
+ int connectResult = SSL_connect(handle_.get());
+ int error = SSL_get_error(handle_.get(), connectResult);
switch (error) {
case SSL_ERROR_NONE: {
- state_ = Connected;
+ state_ = State::Connected;
//std::cout << x->name << std::endl;
- //const char* comp = SSL_get_current_compression(handle_);
+ //const char* comp = SSL_get_current_compression(handle_.get());
//std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl;
onConnected();
break;
}
case SSL_ERROR_WANT_READ:
sendPendingDataToNetwork();
break;
default:
- state_ = Error;
+ state_ = State::Error;
onError(std::make_shared<TLSError>());
}
}
void OpenSSLContext::sendPendingDataToNetwork() {
int size = BIO_pending(writeBIO_);
if (size > 0) {
SafeByteArray data;
data.resize(size);
BIO_read(writeBIO_, vecptr(data), size);
onDataForNetwork(data);
}
}
void OpenSSLContext::handleDataFromNetwork(const SafeByteArray& data) {
BIO_write(readBIO_, vecptr(data), data.size());
switch (state_) {
- case Connecting:
+ case State::Connecting:
doConnect();
break;
- case Connected:
+ case State::Connected:
sendPendingDataToApplication();
break;
- case Start: assert(false); break;
- case Error: /*assert(false);*/ break;
+ case State::Start: assert(false); break;
+ case State::Error: /*assert(false);*/ break;
}
}
void OpenSSLContext::handleDataFromApplication(const SafeByteArray& data) {
- if (SSL_write(handle_, vecptr(data), data.size()) >= 0) {
+ if (SSL_write(handle_.get(), vecptr(data), data.size()) >= 0) {
sendPendingDataToNetwork();
}
else {
- state_ = Error;
+ state_ = State::Error;
onError(std::make_shared<TLSError>());
}
}
void OpenSSLContext::sendPendingDataToApplication() {
SafeByteArray data;
data.resize(SSL_READ_BUFFERSIZE);
- int ret = SSL_read(handle_, vecptr(data), data.size());
+ int ret = SSL_read(handle_.get(), vecptr(data), data.size());
while (ret > 0) {
data.resize(ret);
onDataForApplication(data);
data.resize(SSL_READ_BUFFERSIZE);
- ret = SSL_read(handle_, vecptr(data), data.size());
+ ret = SSL_read(handle_.get(), vecptr(data), data.size());
}
- if (ret < 0 && SSL_get_error(handle_, ret) != SSL_ERROR_WANT_READ) {
- state_ = Error;
+ if (ret < 0 && SSL_get_error(handle_.get(), ret) != SSL_ERROR_WANT_READ) {
+ state_ = State::Error;
onError(std::make_shared<TLSError>());
}
}
bool OpenSSLContext::setClientCertificate(CertificateWithKey::ref certificate) {
std::shared_ptr<PKCS12Certificate> pkcs12Certificate = std::dynamic_pointer_cast<PKCS12Certificate>(certificate);
if (!pkcs12Certificate || pkcs12Certificate->isNull()) {
return false;
}
// Create a PKCS12 structure
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, vecptr(pkcs12Certificate->getData()), pkcs12Certificate->getData().size());
- std::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, NULL), PKCS12_free);
+ std::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, nullptr), PKCS12_free);
BIO_free(bio);
if (!pkcs12) {
return false;
}
// Parse PKCS12
- X509 *certPtr = 0;
- EVP_PKEY* privateKeyPtr = 0;
- STACK_OF(X509)* caCertsPtr = 0;
+ X509 *certPtr = nullptr;
+ EVP_PKEY* privateKeyPtr = nullptr;
+ STACK_OF(X509)* caCertsPtr = nullptr;
SafeByteArray password(pkcs12Certificate->getPassword());
password.push_back(0);
int result = PKCS12_parse(pkcs12.get(), reinterpret_cast<const char*>(vecptr(password)), &privateKeyPtr, &certPtr, &caCertsPtr);
if (result != 1) {
return false;
}
std::shared_ptr<X509> cert(certPtr, X509_free);
std::shared_ptr<EVP_PKEY> privateKey(privateKeyPtr, EVP_PKEY_free);
std::shared_ptr<STACK_OF(X509)> caCerts(caCertsPtr, freeX509Stack);
// Use the key & certificates
- if (SSL_CTX_use_certificate(context_, cert.get()) != 1) {
+ if (SSL_CTX_use_certificate(context_.get(), cert.get()) != 1) {
return false;
}
- if (SSL_CTX_use_PrivateKey(context_, privateKey.get()) != 1) {
+ if (SSL_CTX_use_PrivateKey(context_.get(), privateKey.get()) != 1) {
return false;
}
for (int i = 0; i < sk_X509_num(caCerts.get()); ++i) {
- SSL_CTX_add_extra_chain_cert(context_, sk_X509_value(caCerts.get(), i));
+ SSL_CTX_add_extra_chain_cert(context_.get(), sk_X509_value(caCerts.get(), i));
}
return true;
}
std::vector<Certificate::ref> OpenSSLContext::getPeerCertificateChain() const {
std::vector<Certificate::ref> result;
- STACK_OF(X509)* chain = SSL_get_peer_cert_chain(handle_);
+ STACK_OF(X509)* chain = SSL_get_peer_cert_chain(handle_.get());
for (int i = 0; i < sk_X509_num(chain); ++i) {
std::shared_ptr<X509> x509Cert(X509_dup(sk_X509_value(chain, i)), X509_free);
Certificate::ref cert = std::make_shared<OpenSSLCertificate>(x509Cert);
result.push_back(cert);
}
return result;
}
std::shared_ptr<CertificateVerificationError> OpenSSLContext::getPeerCertificateVerificationError() const {
- int verifyResult = SSL_get_verify_result(handle_);
+ int verifyResult = SSL_get_verify_result(handle_.get());
if (verifyResult != X509_V_OK) {
return std::make_shared<CertificateVerificationError>(getVerificationErrorTypeForResult(verifyResult));
}
else {
return std::shared_ptr<CertificateVerificationError>();
}
}
ByteArray OpenSSLContext::getFinishMessage() const {
ByteArray data;
data.resize(MAX_FINISHED_SIZE);
- size_t size = SSL_get_finished(handle_, vecptr(data), data.size());
+ size_t size = SSL_get_finished(handle_.get(), vecptr(data), data.size());
data.resize(size);
return data;
}
CertificateVerificationError::Type OpenSSLContext::getVerificationErrorTypeForResult(int result) {
assert(result != 0);
switch (result) {
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
return CertificateVerificationError::NotYetValid;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
return CertificateVerificationError::Expired;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
return CertificateVerificationError::SelfSigned;
case X509_V_ERR_CERT_UNTRUSTED:
return CertificateVerificationError::Untrusted;
case X509_V_ERR_CERT_REJECTED:
return CertificateVerificationError::Rejected;
case X509_V_ERR_INVALID_PURPOSE:
return CertificateVerificationError::InvalidPurpose;
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
return CertificateVerificationError::PathLengthExceeded;
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h
index e75b3c9..49ada51 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h
@@ -1,54 +1,73 @@
/*
- * 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 <memory>
+
#include <boost/noncopyable.hpp>
#include <boost/signals2.hpp>
#include <openssl/ssl.h>
#include <Swiften/Base/ByteArray.h>
#include <Swiften/TLS/CertificateWithKey.h>
#include <Swiften/TLS/TLSContext.h>
-namespace Swift {
+namespace std {
+ template<>
+ class default_delete<SSL_CTX> {
+ public:
+ void operator()(SSL_CTX *ptr) {
+ SSL_CTX_free(ptr);
+ }
+ };
+ template<>
+ class default_delete<SSL> {
+ public:
+ void operator()(SSL *ptr) {
+ SSL_free(ptr);
+ }
+ };
+}
+
+namespace Swift {
class OpenSSLContext : public TLSContext, boost::noncopyable {
public:
OpenSSLContext();
- virtual ~OpenSSLContext();
+ virtual ~OpenSSLContext() override final;
- void connect();
- bool setClientCertificate(CertificateWithKey::ref cert);
+ void connect() override final;
+ bool setClientCertificate(CertificateWithKey::ref cert) override final;
- void handleDataFromNetwork(const SafeByteArray&);
- void handleDataFromApplication(const SafeByteArray&);
+ void handleDataFromNetwork(const SafeByteArray&) override final;
+ void handleDataFromApplication(const SafeByteArray&) override final;
- std::vector<Certificate::ref> getPeerCertificateChain() const;
- std::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const;
+ std::vector<Certificate::ref> getPeerCertificateChain() const override final;
+ std::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const override final;
- virtual ByteArray getFinishMessage() const;
+ virtual ByteArray getFinishMessage() const override final;
private:
static void ensureLibraryInitialized();
static CertificateVerificationError::Type getVerificationErrorTypeForResult(int);
void doConnect();
void sendPendingDataToNetwork();
void sendPendingDataToApplication();
private:
- enum State { Start, Connecting, Connected, Error };
+ enum class State { Start, Connecting, Connected, Error };
State state_;
- SSL_CTX* context_;
- SSL* handle_;
- BIO* readBIO_;
- BIO* writeBIO_;
+ std::unique_ptr<SSL_CTX> context_;
+ std::unique_ptr<SSL> handle_;
+ BIO* readBIO_ = nullptr;
+ BIO* writeBIO_ = nullptr;
};
}