summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/TLS')
-rw-r--r--Swiften/TLS/BlindCertificateTrustChecker.h35
-rw-r--r--Swiften/TLS/CAPICertificate.cpp574
-rw-r--r--Swiften/TLS/CAPICertificate.h68
-rw-r--r--Swiften/TLS/Certificate.cpp24
-rw-r--r--Swiften/TLS/Certificate.h51
-rw-r--r--Swiften/TLS/CertificateFactory.cpp27
-rw-r--r--Swiften/TLS/CertificateFactory.h23
-rw-r--r--Swiften/TLS/CertificateTrustChecker.cpp6
-rw-r--r--Swiften/TLS/CertificateTrustChecker.h40
-rw-r--r--Swiften/TLS/CertificateVerificationError.h68
-rw-r--r--Swiften/TLS/CertificateWithKey.h21
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp156
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLCertificate.h121
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp56
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h20
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.cpp1045
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.h101
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp55
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContextFactory.h27
-rw-r--r--Swiften/TLS/PKCS12Certificate.h92
-rw-r--r--Swiften/TLS/PlatformTLSFactories.cpp47
-rw-r--r--Swiften/TLS/PlatformTLSFactories.h30
-rw-r--r--Swiften/TLS/PrivateKey.cpp22
-rw-r--r--Swiften/TLS/PrivateKey.h33
-rw-r--r--Swiften/TLS/SConscript60
-rw-r--r--Swiften/TLS/Schannel/SchannelCertificate.cpp290
-rw-r--r--Swiften/TLS/Schannel/SchannelCertificate.h154
-rw-r--r--Swiften/TLS/Schannel/SchannelCertificateFactory.h12
-rw-r--r--Swiften/TLS/Schannel/SchannelContext.cpp1133
-rw-r--r--Swiften/TLS/Schannel/SchannelContext.h128
-rw-r--r--Swiften/TLS/Schannel/SchannelContextFactory.cpp31
-rw-r--r--Swiften/TLS/Schannel/SchannelContextFactory.h30
-rw-r--r--Swiften/TLS/Schannel/SchannelUtil.h820
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportCertificate.h46
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportCertificate.mm152
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportCertificateFactory.h20
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportContext.h58
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportContext.mm507
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp46
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportContextFactory.h31
-rw-r--r--Swiften/TLS/ServerIdentityVerifier.cpp128
-rw-r--r--Swiften/TLS/ServerIdentityVerifier.h36
-rw-r--r--Swiften/TLS/SimpleCertificate.h134
-rw-r--r--Swiften/TLS/TLSContext.cpp44
-rw-r--r--Swiften/TLS/TLSContext.h69
-rw-r--r--Swiften/TLS/TLSContextFactory.cpp13
-rw-r--r--Swiften/TLS/TLSContextFactory.h30
-rw-r--r--Swiften/TLS/TLSError.h46
-rw-r--r--Swiften/TLS/TLSOptions.h77
-rw-r--r--Swiften/TLS/UnitTest/CertificateTest.cpp141
-rw-r--r--Swiften/TLS/UnitTest/ClientServerTest.cpp883
-rw-r--r--Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp356
52 files changed, 5587 insertions, 2630 deletions
diff --git a/Swiften/TLS/BlindCertificateTrustChecker.h b/Swiften/TLS/BlindCertificateTrustChecker.h
index d91ec25..76a7a02 100644
--- a/Swiften/TLS/BlindCertificateTrustChecker.h
+++ b/Swiften/TLS/BlindCertificateTrustChecker.h
@@ -1,26 +1,27 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
+#include <Swiften/Base/API.h>
#include <Swiften/TLS/CertificateTrustChecker.h>
namespace Swift {
- /**
- * A certificate trust checker that trusts any ceritficate.
- *
- * This can be used to ignore any TLS certificate errors occurring
- * during connection.
- *
- * \see Client::setAlwaysTrustCertificates()
- */
- class BlindCertificateTrustChecker : public CertificateTrustChecker {
- public:
- virtual bool isCertificateTrusted(const std::vector<Certificate::ref>&) {
- return true;
- }
- };
+ /**
+ * A certificate trust checker that trusts any ceritficate.
+ *
+ * This can be used to ignore any TLS certificate errors occurring
+ * during connection.
+ *
+ * \see Client::setAlwaysTrustCertificates()
+ */
+ class SWIFTEN_API BlindCertificateTrustChecker : public CertificateTrustChecker {
+ public:
+ virtual bool isCertificateTrusted(const std::vector<Certificate::ref>&) {
+ return true;
+ }
+ };
}
diff --git a/Swiften/TLS/CAPICertificate.cpp b/Swiften/TLS/CAPICertificate.cpp
index d10ae6d..526b535 100644
--- a/Swiften/TLS/CAPICertificate.cpp
+++ b/Swiften/TLS/CAPICertificate.cpp
@@ -1,332 +1,342 @@
/*
- * Copyright (c) 2012 Isode Limited, London, England.
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
+ * Copyright (c) 2012-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <Swiften/Network/TimerFactory.h>
#include <Swiften/TLS/CAPICertificate.h>
-#include <Swiften/StringCodecs/Hexify.h>
-#include <Swiften/Base/Log.h>
-#include <boost/bind.hpp>
#include <boost/algorithm/string/predicate.hpp>
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/TimerFactory.h>
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/TLS/Schannel/SchannelUtil.h>
// Size of the SHA1 hash
#define SHA1_HASH_LEN 20
+#define DEBUG_SCARD_STATUS(function, status) \
+{ \
+ std::shared_ptr<boost::system::error_code> errorCode = std::make_shared<boost::system::error_code>(status, boost::system::system_category()); \
+ SWIFT_LOG(debug) << std::hex << function << ": status: 0x" << status << ": " << errorCode->message(); \
+}
namespace Swift {
-CAPICertificate::CAPICertificate(const std::string& capiUri, TimerFactory* timerFactory) :
- valid_(false),
- uri_(capiUri),
- certStoreHandle_(0),
- scardContext_(0),
- cardHandle_(0),
- certStore_(),
- certName_(),
- smartCardReaderName_(),
- timerFactory_(timerFactory),
- lastPollingResult_(true) {
- assert(timerFactory_);
-
- setUri(capiUri);
+CAPICertificate::CAPICertificate(const std::string& capiUri, TimerFactory* timerFactory) :
+ valid_(false),
+ uri_(capiUri),
+ certStoreHandle_(0),
+ scardContext_(0),
+ cardHandle_(0),
+ certStore_(),
+ certName_(),
+ smartCardReaderName_(),
+ timerFactory_(timerFactory),
+ lastPollingResult_(true) {
+ assert(timerFactory_);
+
+ setUri(capiUri);
}
CAPICertificate::~CAPICertificate() {
- if (smartCardTimer_) {
- smartCardTimer_->stop();
- smartCardTimer_->onTick.disconnect(boost::bind(&CAPICertificate::handleSmartCardTimerTick, this));
- smartCardTimer_.reset();
- }
-
- if (certStoreHandle_) {
- CertCloseStore(certStoreHandle_, 0);
- }
-
- if (cardHandle_) {
- (void) SCardDisconnect(cardHandle_, SCARD_LEAVE_CARD);
- }
-
- if (scardContext_) {
- SCardReleaseContext(scardContext_);
- }
+ SWIFT_LOG(debug) << "Destroying the CAPICertificate";
+ if (smartCardTimer_) {
+ smartCardTimer_->stop();
+ smartCardTimer_->onTick.disconnect(boost::bind(&CAPICertificate::handleSmartCardTimerTick, this));
+ smartCardTimer_.reset();
+ }
+
+ if (certStoreHandle_) {
+ if (CertCloseStore(certStoreHandle_, 0) == FALSE) {
+ SWIFT_LOG(debug) << "Failed to close the certificate store handle";
+ }
+ }
+
+ if (cardHandle_) {
+ LONG result = SCardDisconnect(cardHandle_, SCARD_LEAVE_CARD);
+ DEBUG_SCARD_STATUS("SCardDisconnect", result);
+ }
+
+ if (scardContext_) {
+ LONG result = SCardReleaseContext(scardContext_);
+ DEBUG_SCARD_STATUS("SCardReleaseContext", result);
+ }
}
bool CAPICertificate::isNull() const {
- return uri_.empty() || !valid_;
+ return uri_.empty() || !valid_;
}
const std::string& CAPICertificate::getCertStoreName() const {
- return certStore_;
+ return certStore_;
}
const std::string& CAPICertificate::getCertName() const {
- return certName_;
+ return certName_;
}
const std::string& CAPICertificate::getSmartCardReaderName() const {
- return smartCardReaderName_;
+ return smartCardReaderName_;
}
-PCCERT_CONTEXT findCertificateInStore (HCERTSTORE certStoreHandle, const std::string &certName) {
- if (!boost::iequals(certName.substr(0, 5), "sha1:")) {
+PCCERT_CONTEXT findCertificateInStore(HCERTSTORE certStoreHandle, const std::string &certName) {
+ if (!boost::iequals(certName.substr(0, 5), "sha1:")) {
- // Find client certificate. Note that this sample just searches for a
- // certificate that contains the user name somewhere in the subject name.
- return CertFindCertificateInStore(certStoreHandle, X509_ASN_ENCODING, /*dwFindFlags*/ 0, CERT_FIND_SUBJECT_STR_A, /* *pvFindPara*/certName.c_str(), /*pPrevCertContext*/ NULL);
- }
+ // Find client certificate. Note that this sample just searches for a
+ // certificate that contains the user name somewhere in the subject name.
+ return CertFindCertificateInStore(certStoreHandle, X509_ASN_ENCODING, /*dwFindFlags*/ 0, CERT_FIND_SUBJECT_STR_A, /* *pvFindPara*/certName.c_str(), /*pPrevCertContext*/ NULL);
+ }
- std::string hexstring = certName.substr(5);
- ByteArray byteArray = Hexify::unhexify(hexstring);
- CRYPT_HASH_BLOB HashBlob;
+ std::string hexstring = certName.substr(5);
+ ByteArray byteArray = Hexify::unhexify(hexstring);
+ CRYPT_HASH_BLOB HashBlob;
- if (byteArray.size() != SHA1_HASH_LEN) {
- return NULL;
- }
- HashBlob.cbData = SHA1_HASH_LEN;
- HashBlob.pbData = static_cast<BYTE *>(vecptr(byteArray));
+ if (byteArray.size() != SHA1_HASH_LEN) {
+ return NULL;
+ }
+ HashBlob.cbData = SHA1_HASH_LEN;
+ HashBlob.pbData = static_cast<BYTE *>(vecptr(byteArray));
- // Find client certificate. Note that this sample just searches for a
- // certificate that contains the user name somewhere in the subject name.
- return CertFindCertificateInStore(certStoreHandle, X509_ASN_ENCODING, /* dwFindFlags */ 0, CERT_FIND_HASH, &HashBlob, /* pPrevCertContext */ NULL);
+ // Find client certificate. Note that this sample just searches for a
+ // certificate that contains the user name somewhere in the subject name.
+ return CertFindCertificateInStore(certStoreHandle, X509_ASN_ENCODING, /* dwFindFlags */ 0, CERT_FIND_HASH, &HashBlob, /* pPrevCertContext */ NULL);
}
-
-void CAPICertificate::setUri (const std::string& capiUri) {
- valid_ = false;
-
- /* Syntax: "certstore:" <cert_store> ":" <hash> ":" <hash_of_cert> */
-
- if (!boost::iequals(capiUri.substr(0, 10), "certstore:")) {
- return;
- }
-
- /* Substring of subject: uses "storename" */
- std::string capiIdentity = capiUri.substr(10);
- std::string newCertStoreName;
- size_t pos = capiIdentity.find_first_of (':');
-
- if (pos == std::string::npos) {
- /* Using the default certificate store */
- newCertStoreName = "MY";
- certName_ = capiIdentity;
- }
- else {
- newCertStoreName = capiIdentity.substr(0, pos);
- certName_ = capiIdentity.substr(pos + 1);
- }
-
- if (certStoreHandle_ != NULL) {
- if (newCertStoreName != certStore_) {
- CertCloseStore(certStoreHandle_, 0);
- certStoreHandle_ = NULL;
- }
- }
-
- if (certStoreHandle_ == NULL) {
- certStoreHandle_ = CertOpenSystemStore(0, newCertStoreName.c_str());
- if (!certStoreHandle_) {
- return;
- }
- }
-
- certStore_ = newCertStoreName;
-
- PCCERT_CONTEXT certContext = findCertificateInStore (certStoreHandle_, certName_);
-
- if (!certContext) {
- return;
- }
-
-
- /* Now verify that we can have access to the corresponding private key */
-
- DWORD len;
- CRYPT_KEY_PROV_INFO *pinfo;
- HCRYPTPROV hprov;
- HCRYPTKEY key;
-
- if (!CertGetCertificateContextProperty(certContext,
- CERT_KEY_PROV_INFO_PROP_ID,
- NULL,
- &len)) {
- CertFreeCertificateContext(certContext);
- return;
- }
-
- pinfo = static_cast<CRYPT_KEY_PROV_INFO *>(malloc(len));
- if (!pinfo) {
- CertFreeCertificateContext(certContext);
- return;
- }
-
- if (!CertGetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, pinfo, &len)) {
- CertFreeCertificateContext(certContext);
- free(pinfo);
- return;
- }
-
- CertFreeCertificateContext(certContext);
-
- // Now verify if we have access to the private key
- if (!CryptAcquireContextW(&hprov, pinfo->pwszContainerName, pinfo->pwszProvName, pinfo->dwProvType, 0)) {
- free(pinfo);
- return;
- }
-
-
- char smartCardReader[1024];
- DWORD bufferLength = sizeof(smartCardReader);
- if (!CryptGetProvParam(hprov, PP_SMARTCARD_READER, (BYTE *)&smartCardReader, &bufferLength, 0)) {
- DWORD error = GetLastError();
- smartCardReaderName_ = "";
- }
- else {
- smartCardReaderName_ = smartCardReader;
-
- LONG result = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &scardContext_);
- if (SCARD_S_SUCCESS == result) {
- // Initiate monitoring for smartcard ejection
- smartCardTimer_ = timerFactory_->createTimer(SMARTCARD_EJECTION_CHECK_FREQUENCY_MILLISECONDS);
- }
- else {
- ///Need to handle an error here
- }
- }
-
- if (!CryptGetUserKey(hprov, pinfo->dwKeySpec, &key)) {
- CryptReleaseContext(hprov, 0);
- free(pinfo);
- return;
- }
-
- CryptDestroyKey(key);
- CryptReleaseContext(hprov, 0);
- free(pinfo);
-
- if (smartCardTimer_) {
- smartCardTimer_->onTick.connect(boost::bind(&CAPICertificate::handleSmartCardTimerTick, this));
- smartCardTimer_->start();
- }
-
- valid_ = true;
+void CAPICertificate::setUri(const std::string& capiUri) {
+ valid_ = false;
+
+ /* Syntax: "certstore:" <cert_store> ":" <hash> ":" <hash_of_cert> */
+
+ if (!boost::iequals(capiUri.substr(0, 10), "certstore:")) {
+ return;
+ }
+
+ /* Substring of subject: uses "storename" */
+ std::string capiIdentity = capiUri.substr(10);
+ std::string newCertStoreName;
+ size_t pos = capiIdentity.find_first_of(':');
+
+ if (pos == std::string::npos) {
+ /* Using the default certificate store */
+ newCertStoreName = "MY";
+ certName_ = capiIdentity;
+ }
+ else {
+ newCertStoreName = capiIdentity.substr(0, pos);
+ certName_ = capiIdentity.substr(pos + 1);
+ }
+
+ if (certStoreHandle_ != NULL) {
+ if (newCertStoreName != certStore_) {
+ CertCloseStore(certStoreHandle_, 0);
+ certStoreHandle_ = NULL;
+ }
+ }
+
+ if (certStoreHandle_ == NULL) {
+ certStoreHandle_ = CertOpenSystemStore(0, newCertStoreName.c_str());
+ if (!certStoreHandle_) {
+ return;
+ }
+ }
+
+ certStore_ = newCertStoreName;
+
+ ScopedCertContext certContext(findCertificateInStore(certStoreHandle_, certName_));
+ if (!certContext) {
+ return;
+ }
+
+ /* Now verify that we can have access to the corresponding private key */
+
+ DWORD len;
+ if (!CertGetCertificateContextProperty(certContext,
+ CERT_KEY_PROV_INFO_PROP_ID,
+ NULL,
+ &len)) {
+ SWIFT_LOG(error) << "Error while retrieving context properties";
+ return;
+ }
+
+ std::shared_ptr<CRYPT_KEY_PROV_INFO> pinfo(static_cast<CRYPT_KEY_PROV_INFO *>(malloc(len)), free);
+ if (!pinfo) {
+ return;
+ }
+
+ if (!CertGetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, pinfo.get(), &len)) {
+ return;
+ }
+ certContext.FreeContext();
+
+ HCRYPTPROV hprov;
+ // Now verify if we have access to the private key
+ if (!CryptAcquireContextW(&hprov, pinfo->pwszContainerName, pinfo->pwszProvName, pinfo->dwProvType, 0)) {
+ return;
+ }
+
+ char smartCardReader[1024];
+ DWORD bufferLength = sizeof(smartCardReader);
+ if (!CryptGetProvParam(hprov, PP_SMARTCARD_READER, (BYTE *)&smartCardReader, &bufferLength, 0)) {
+ DWORD error = GetLastError();
+ smartCardReaderName_ = "";
+ }
+ else {
+ smartCardReaderName_ = smartCardReader;
+
+ LONG result = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &scardContext_);
+ DEBUG_SCARD_STATUS("SCardEstablishContext", result);
+ if (SCARD_S_SUCCESS == result) {
+ // Initiate monitoring for smartcard ejection
+ smartCardTimer_ = timerFactory_->createTimer(SMARTCARD_EJECTION_CHECK_FREQUENCY_MILLISECONDS);
+ }
+ else {
+ CryptReleaseContext(hprov, 0);
+ return;
+ }
+ }
+
+ HCRYPTKEY key;
+ if (!CryptGetUserKey(hprov, pinfo->dwKeySpec, &key)) {
+ CryptReleaseContext(hprov, 0);
+ return;
+ }
+
+ CryptDestroyKey(key);
+ CryptReleaseContext(hprov, 0);
+
+ if (smartCardTimer_) {
+ smartCardTimer_->onTick.connect(boost::bind(&CAPICertificate::handleSmartCardTimerTick, this));
+ smartCardTimer_->start();
+ }
+
+ valid_ = true;
}
-static void smartcard_check_status (SCARDCONTEXT hContext,
- const char* pReader,
- SCARDHANDLE hCardHandle, /* Can be 0 on the first call */
- SCARDHANDLE* newCardHandle, /* The handle returned */
- DWORD* pdwState) {
- if (hCardHandle == 0) {
- DWORD dwAP;
- LONG result = SCardConnect(hContext, pReader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCardHandle, &dwAP);
- if (SCARD_S_SUCCESS != result) {
- hCardHandle = 0;
- if (SCARD_E_NO_SMARTCARD == result || SCARD_W_REMOVED_CARD == result) {
- *pdwState = SCARD_ABSENT;
- }
- else {
- *pdwState = SCARD_UNKNOWN;
- }
-
- if (newCardHandle == NULL) {
- (void) SCardDisconnect(hCardHandle, SCARD_LEAVE_CARD);
- hCardHandle = 0;
- }
- else {
- *newCardHandle = hCardHandle;
- }
- }
- }
-
- char szReader[200];
- DWORD cch = sizeof(szReader);
- BYTE bAttr[32];
- DWORD cByte = 32;
- LONG result = SCardStatus(hCardHandle, /* Unfortunately we can't use NULL here */ szReader, &cch, pdwState, NULL, (LPBYTE)&bAttr, &cByte);
-
- if (SCARD_S_SUCCESS != result) {
- if (SCARD_E_NO_SMARTCARD == result || SCARD_W_REMOVED_CARD == result) {
- *pdwState = SCARD_ABSENT;
- }
- else {
- *pdwState = SCARD_UNKNOWN;
- }
- }
-
- if (newCardHandle == NULL) {
- (void) SCardDisconnect(hCardHandle, SCARD_LEAVE_CARD);
- hCardHandle = 0;
- }
- else {
- *newCardHandle = hCardHandle;
- }
+static void smartcard_check_status(SCARDCONTEXT hContext,
+ const char* pReader,
+ SCARDHANDLE hCardHandle, /* Can be 0 on the first call */
+ SCARDHANDLE* newCardHandle, /* The handle returned */
+ DWORD* pdwState) {
+ DWORD shareMode = SCARD_SHARE_SHARED;
+ DWORD preferredProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
+ DWORD dwAP;
+ LONG result;
+
+ if (hCardHandle == 0) {
+ result = SCardConnect(hContext, pReader, shareMode, preferredProtocols, &hCardHandle, &dwAP);
+ DEBUG_SCARD_STATUS("SCardConnect", result);
+ if (SCARD_S_SUCCESS != result) {
+ hCardHandle = 0;
+ }
+ }
+
+ char szReader[200];
+ DWORD cch = sizeof(szReader);
+ BYTE bAttr[32];
+ DWORD cByte = 32;
+ size_t countStatusAttempts = 0;
+
+ while (hCardHandle && (countStatusAttempts < 2)) {
+ *pdwState = SCARD_UNKNOWN;
+
+ result = SCardStatus(hCardHandle, /* Unfortunately we can't use NULL here */ szReader, &cch, pdwState, NULL, (LPBYTE)&bAttr, &cByte);
+ DEBUG_SCARD_STATUS("SCardStatus", result);
+ countStatusAttempts++;
+
+ if ((SCARD_W_RESET_CARD == result) && (countStatusAttempts < 2)) {
+ result = SCardReconnect(hCardHandle, shareMode, preferredProtocols, SCARD_RESET_CARD, &dwAP);
+ DEBUG_SCARD_STATUS("SCardReconnect", result);
+ if (SCARD_S_SUCCESS != result) {
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ }
+
+ if (SCARD_S_SUCCESS != result) {
+ if (SCARD_E_NO_SMARTCARD == result || SCARD_W_REMOVED_CARD == result) {
+ *pdwState = SCARD_ABSENT;
+ }
+ else {
+ *pdwState = SCARD_UNKNOWN;
+ }
+ }
+
+ if (newCardHandle == NULL) {
+ result = SCardDisconnect(hCardHandle, SCARD_LEAVE_CARD);
+ DEBUG_SCARD_STATUS("SCardDisconnect", result);
+ }
+ else {
+ *newCardHandle = hCardHandle;
+ }
}
-bool CAPICertificate::checkIfSmartCardPresent () {
- if (!smartCardReaderName_.empty()) {
- DWORD dwState;
- smartcard_check_status(scardContext_, smartCardReaderName_.c_str(), cardHandle_, &cardHandle_, &dwState);
-
- switch (dwState) {
- case SCARD_ABSENT:
- SWIFT_LOG(debug) << "Card absent." << std::endl;
- break;
- case SCARD_PRESENT:
- SWIFT_LOG(debug) << "Card present." << std::endl;
- break;
- case SCARD_SWALLOWED:
- SWIFT_LOG(debug) << "Card swallowed." << std::endl;
- break;
- case SCARD_POWERED:
- SWIFT_LOG(debug) << "Card has power." << std::endl;
- break;
- case SCARD_NEGOTIABLE:
- SWIFT_LOG(debug) << "Card reset and waiting PTS negotiation." << std::endl;
- break;
- case SCARD_SPECIFIC:
- SWIFT_LOG(debug) << "Card has specific communication protocols set." << std::endl;
- break;
- default:
- SWIFT_LOG(debug) << "Unknown or unexpected card state." << std::endl;
- break;
- }
-
-
-
- switch (dwState) {
- case SCARD_ABSENT:
- return false;
-
- case SCARD_PRESENT:
- case SCARD_SWALLOWED:
- case SCARD_POWERED:
- case SCARD_NEGOTIABLE:
- case SCARD_SPECIFIC:
- return true;
-
- default:
- return false;
- }
- }
- else {
- return false;
- }
+bool CAPICertificate::checkIfSmartCardPresent() {
+ if (!smartCardReaderName_.empty()) {
+ DWORD dwState;
+ smartcard_check_status(scardContext_, smartCardReaderName_.c_str(), cardHandle_, &cardHandle_, &dwState);
+
+ switch (dwState) {
+ case SCARD_ABSENT:
+ SWIFT_LOG(debug) << "Card absent.";
+ break;
+ case SCARD_PRESENT:
+ SWIFT_LOG(debug) << "Card present.";
+ break;
+ case SCARD_SWALLOWED:
+ SWIFT_LOG(debug) << "Card swallowed.";
+ break;
+ case SCARD_POWERED:
+ SWIFT_LOG(debug) << "Card has power.";
+ break;
+ case SCARD_NEGOTIABLE:
+ SWIFT_LOG(debug) << "Card reset and waiting PTS negotiation.";
+ break;
+ case SCARD_SPECIFIC:
+ SWIFT_LOG(debug) << "Card has specific communication protocols set.";
+ break;
+ default:
+ SWIFT_LOG(debug) << "Unknown or unexpected card state.";
+ break;
+ }
+
+ switch (dwState) {
+ case SCARD_ABSENT:
+ return false;
+
+ case SCARD_PRESENT:
+ case SCARD_SWALLOWED:
+ case SCARD_POWERED:
+ case SCARD_NEGOTIABLE:
+ case SCARD_SPECIFIC:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
}
void CAPICertificate::handleSmartCardTimerTick() {
- bool poll = checkIfSmartCardPresent();
- if (lastPollingResult_ && !poll) {
- onCertificateCardRemoved();
- }
- lastPollingResult_ = poll;
- smartCardTimer_->start();
+ bool poll = checkIfSmartCardPresent();
+ if (lastPollingResult_ && !poll) {
+ SWIFT_LOG(debug) << "CAPI Certificate detected that the certificate card was removed";
+ onCertificateCardRemoved();
+ }
+ lastPollingResult_ = poll;
+ smartCardTimer_->start();
}
}
diff --git a/Swiften/TLS/CAPICertificate.h b/Swiften/TLS/CAPICertificate.h
index aebfb41..0259db5 100644
--- a/Swiften/TLS/CAPICertificate.h
+++ b/Swiften/TLS/CAPICertificate.h
@@ -4,10 +4,16 @@
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
#pragma once
#include <Swiften/Base/API.h>
-#include <Swiften/Base/boost_bsignals.h>
+#include <boost/signals2.hpp>
#include <Swiften/Base/SafeByteArray.h>
#include <Swiften/TLS/CertificateWithKey.h>
#include <Swiften/Network/Timer.h>
@@ -17,52 +23,52 @@
#include <WinCrypt.h>
#include <Winscard.h>
-#define SMARTCARD_EJECTION_CHECK_FREQUENCY_MILLISECONDS 1000
+#define SMARTCARD_EJECTION_CHECK_FREQUENCY_MILLISECONDS 1000
namespace Swift {
- class TimerFactory;
+ class TimerFactory;
- class SWIFTEN_API CAPICertificate : public Swift::CertificateWithKey {
- public:
- CAPICertificate(const std::string& capiUri, TimerFactory* timerFactory);
+ class SWIFTEN_API CAPICertificate : public Swift::CertificateWithKey {
+ public:
+ CAPICertificate(const std::string& capiUri, TimerFactory* timerFactory);
- virtual ~CAPICertificate();
+ virtual ~CAPICertificate();
- virtual bool isNull() const;
+ virtual bool isNull() const;
- const std::string& getCertStoreName() const;
+ const std::string& getCertStoreName() const;
- const std::string& getCertName() const;
+ const std::string& getCertName() const;
- const std::string& getSmartCardReaderName() const;
+ const std::string& getSmartCardReaderName() const;
- public:
- boost::signal<void ()> onCertificateCardRemoved;
+ public:
+ boost::signals2::signal<void ()> onCertificateCardRemoved;
- private:
- void setUri (const std::string& capiUri);
+ private:
+ void setUri (const std::string& capiUri);
- void handleSmartCardTimerTick();
+ void handleSmartCardTimerTick();
- bool checkIfSmartCardPresent();
+ bool checkIfSmartCardPresent();
- private:
- bool valid_;
- std::string uri_;
+ private:
+ bool valid_;
+ std::string uri_;
- HCERTSTORE certStoreHandle_;
- SCARDCONTEXT scardContext_;
- SCARDHANDLE cardHandle_;
+ HCERTSTORE certStoreHandle_;
+ SCARDCONTEXT scardContext_;
+ SCARDHANDLE cardHandle_;
- /* Parsed components of the uri_ */
- std::string certStore_;
- std::string certName_;
- std::string smartCardReaderName_;
- boost::shared_ptr<Timer> smartCardTimer_;
- TimerFactory* timerFactory_;
+ /* Parsed components of the uri_ */
+ std::string certStore_;
+ std::string certName_;
+ std::string smartCardReaderName_;
+ std::shared_ptr<Timer> smartCardTimer_;
+ TimerFactory* timerFactory_;
- bool lastPollingResult_;
- };
+ bool lastPollingResult_;
+ };
PCCERT_CONTEXT findCertificateInStore (HCERTSTORE certStoreHandle, const std::string &certName);
diff --git a/Swiften/TLS/Certificate.cpp b/Swiften/TLS/Certificate.cpp
index ec268c8..c7d48b2 100644
--- a/Swiften/TLS/Certificate.cpp
+++ b/Swiften/TLS/Certificate.cpp
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2010-2013 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2013 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <Swiften/TLS/Certificate.h>
@@ -20,15 +20,15 @@ Certificate::~Certificate() {
}
std::string Certificate::getSHA1Fingerprint(Certificate::ref certificate, CryptoProvider* crypto) {
- ByteArray hash = crypto->getSHA1Hash(certificate->toDER());
- std::ostringstream s;
- for (size_t i = 0; i < hash.size(); ++i) {
- if (i > 0) {
- s << ":";
- }
- s << Hexify::hexify(hash[i]);
- }
- return std::string(s.str());
+ ByteArray hash = crypto->getSHA1Hash(certificate->toDER());
+ std::ostringstream s;
+ for (size_t i = 0; i < hash.size(); ++i) {
+ if (i > 0) {
+ s << ":";
+ }
+ s << Hexify::hexify(hash[i]);
+ }
+ return std::string(s.str());
}
}
diff --git a/Swiften/TLS/Certificate.h b/Swiften/TLS/Certificate.h
index f558c12..dbc61ad 100644
--- a/Swiften/TLS/Certificate.h
+++ b/Swiften/TLS/Certificate.h
@@ -1,44 +1,45 @@
/*
- * Copyright (c) 2010-2013 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <boost/shared_ptr.hpp>
-
+#include <memory>
#include <string>
+#include <vector>
+
#include <Swiften/Base/API.h>
#include <Swiften/Base/ByteArray.h>
namespace Swift {
- class CryptoProvider;
+ class CryptoProvider;
- class SWIFTEN_API Certificate {
- public:
- typedef boost::shared_ptr<Certificate> ref;
+ class SWIFTEN_API Certificate {
+ public:
+ typedef std::shared_ptr<Certificate> ref;
- virtual ~Certificate();
+ virtual ~Certificate();
- /**
- * Returns the textual representation of the full Subject
- * name.
- */
- virtual std::string getSubjectName() const = 0;
+ /**
+ * Returns the textual representation of the full Subject
+ * name.
+ */
+ virtual std::string getSubjectName() const = 0;
- virtual std::vector<std::string> getCommonNames() const = 0;
- virtual std::vector<std::string> getSRVNames() const = 0;
- virtual std::vector<std::string> getDNSNames() const = 0;
- virtual std::vector<std::string> getXMPPAddresses() const = 0;
+ virtual std::vector<std::string> getCommonNames() const = 0;
+ virtual std::vector<std::string> getSRVNames() const = 0;
+ virtual std::vector<std::string> getDNSNames() const = 0;
+ virtual std::vector<std::string> getXMPPAddresses() const = 0;
- virtual ByteArray toDER() const = 0;
+ virtual ByteArray toDER() const = 0;
- static std::string getSHA1Fingerprint(Certificate::ref, CryptoProvider* crypto);
+ static std::string getSHA1Fingerprint(Certificate::ref, CryptoProvider* crypto);
- protected:
- static const char* ID_ON_XMPPADDR_OID;
- static const char* ID_ON_DNSSRV_OID;
+ protected:
+ static const char* ID_ON_XMPPADDR_OID;
+ static const char* ID_ON_DNSSRV_OID;
- };
+ };
}
diff --git a/Swiften/TLS/CertificateFactory.cpp b/Swiften/TLS/CertificateFactory.cpp
index df01090..d4db3f4 100644
--- a/Swiften/TLS/CertificateFactory.cpp
+++ b/Swiften/TLS/CertificateFactory.cpp
@@ -1,14 +1,35 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <Swiften/TLS/CertificateFactory.h>
+#include <cassert>
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/optional.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/StringCodecs/Base64.h>
+#include <Swiften/TLS/PrivateKey.h>
+
namespace Swift {
CertificateFactory::~CertificateFactory() {
}
+std::vector<std::shared_ptr<Certificate>> CertificateFactory::createCertificateChain(const ByteArray& /* data */) {
+ assert(false);
+ return std::vector<std::shared_ptr<Certificate>>();
+}
+
+PrivateKey::ref CertificateFactory::createPrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password) {
+ return std::make_shared<PrivateKey>(data, password);
+}
+
}
diff --git a/Swiften/TLS/CertificateFactory.h b/Swiften/TLS/CertificateFactory.h
index 3e94082..873c36b 100644
--- a/Swiften/TLS/CertificateFactory.h
+++ b/Swiften/TLS/CertificateFactory.h
@@ -1,18 +1,25 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
+#include <boost/optional.hpp>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Base/SafeByteArray.h>
#include <Swiften/TLS/Certificate.h>
+#include <Swiften/TLS/PrivateKey.h>
namespace Swift {
- class CertificateFactory {
- public:
- virtual ~CertificateFactory();
+ class SWIFTEN_API CertificateFactory {
+ public:
+ virtual ~CertificateFactory();
- virtual Certificate::ref createCertificateFromDER(const ByteArray& der) = 0;
- };
+ virtual Certificate* createCertificateFromDER(const ByteArray& der) = 0;
+ virtual std::vector<std::shared_ptr<Certificate>> createCertificateChain(const ByteArray& data);
+ PrivateKey::ref createPrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password = boost::optional<SafeByteArray>());
+ };
}
diff --git a/Swiften/TLS/CertificateTrustChecker.cpp b/Swiften/TLS/CertificateTrustChecker.cpp
index 42e24a1..fa3761a 100644
--- a/Swiften/TLS/CertificateTrustChecker.cpp
+++ b/Swiften/TLS/CertificateTrustChecker.cpp
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <Swiften/TLS/CertificateTrustChecker.h>
diff --git a/Swiften/TLS/CertificateTrustChecker.h b/Swiften/TLS/CertificateTrustChecker.h
index 4ec0b39..dd2b3ec 100644
--- a/Swiften/TLS/CertificateTrustChecker.h
+++ b/Swiften/TLS/CertificateTrustChecker.h
@@ -1,12 +1,12 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <boost/shared_ptr.hpp>
+#include <memory>
#include <string>
#include <vector>
@@ -14,21 +14,21 @@
#include <Swiften/TLS/Certificate.h>
namespace Swift {
- /**
- * A class to implement a check for certificate trust.
- */
- class SWIFTEN_API CertificateTrustChecker {
- public:
- virtual ~CertificateTrustChecker();
+ /**
+ * A class to implement a check for certificate trust.
+ */
+ class SWIFTEN_API CertificateTrustChecker {
+ public:
+ virtual ~CertificateTrustChecker();
- /**
- * This method is called to find out whether a certificate (chain) is
- * trusted. This usually happens when a certificate's validation
- * fails, to check whether to proceed with the connection or not.
- *
- * certificateChain contains the chain of certificates. The first certificate
- * is the subject certificate.
- */
- virtual bool isCertificateTrusted(const std::vector<Certificate::ref>& certificateChain) = 0;
- };
+ /**
+ * This method is called to find out whether a certificate (chain) is
+ * trusted. This usually happens when a certificate's validation
+ * fails, to check whether to proceed with the connection or not.
+ *
+ * certificateChain contains the chain of certificates. The first certificate
+ * is the subject certificate.
+ */
+ virtual bool isCertificateTrusted(const std::vector<Certificate::ref>& certificateChain) = 0;
+ };
}
diff --git a/Swiften/TLS/CertificateVerificationError.h b/Swiften/TLS/CertificateVerificationError.h
index b17f5df..02b4cca 100644
--- a/Swiften/TLS/CertificateVerificationError.h
+++ b/Swiften/TLS/CertificateVerificationError.h
@@ -1,42 +1,44 @@
/*
- * Copyright (c) 2010-2012 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <boost/shared_ptr.hpp>
+#include <memory>
+
+#include <Swiften/Base/API.h>
#include <Swiften/Base/Error.h>
namespace Swift {
- class CertificateVerificationError : public Error {
- public:
- typedef boost::shared_ptr<CertificateVerificationError> ref;
-
- enum Type {
- UnknownError,
- Expired,
- NotYetValid,
- SelfSigned,
- Rejected,
- Untrusted,
- InvalidPurpose,
- PathLengthExceeded,
- InvalidSignature,
- InvalidCA,
- InvalidServerIdentity,
- Revoked,
- RevocationCheckFailed
- };
-
- CertificateVerificationError(Type type = UnknownError) : type(type) {}
-
- Type getType() const {
- return type;
- }
-
- private:
- Type type;
- };
+ class SWIFTEN_API CertificateVerificationError : public Error {
+ public:
+ typedef std::shared_ptr<CertificateVerificationError> ref;
+
+ enum Type {
+ UnknownError,
+ Expired,
+ NotYetValid,
+ SelfSigned,
+ Rejected,
+ Untrusted,
+ InvalidPurpose,
+ PathLengthExceeded,
+ InvalidSignature,
+ InvalidCA,
+ InvalidServerIdentity,
+ Revoked,
+ RevocationCheckFailed
+ };
+
+ CertificateVerificationError(Type type = UnknownError) : type(type) {}
+
+ Type getType() const {
+ return type;
+ }
+
+ private:
+ Type type;
+ };
}
diff --git a/Swiften/TLS/CertificateWithKey.h b/Swiften/TLS/CertificateWithKey.h
index 1bdeb03..8414938 100644
--- a/Swiften/TLS/CertificateWithKey.h
+++ b/Swiften/TLS/CertificateWithKey.h
@@ -1,22 +1,23 @@
/*
- * Copyright (c) 2010-2012 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
+#include <Swiften/Base/API.h>
#include <Swiften/Base/SafeByteArray.h>
namespace Swift {
- class CertificateWithKey {
- public:
- typedef boost::shared_ptr<CertificateWithKey> ref;
- CertificateWithKey() {}
+ class SWIFTEN_API CertificateWithKey {
+ public:
+ typedef std::shared_ptr<CertificateWithKey> ref;
+ CertificateWithKey() {}
- virtual ~CertificateWithKey() {}
+ virtual ~CertificateWithKey() {}
- virtual bool isNull() const = 0;
+ virtual bool isNull() const = 0;
- };
+ };
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp
index d654787..66b650d 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2010-2013 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <Swiften/TLS/OpenSSL/OpenSSLCertificate.h>
@@ -19,92 +19,100 @@
namespace Swift {
-OpenSSLCertificate::OpenSSLCertificate(boost::shared_ptr<X509> cert) : cert(cert) {
- parse();
+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));
+ unsigned char* p = const_cast<unsigned char*>(vecptr(der));
#else
- const unsigned char* p = vecptr(der);
+ const unsigned char* p = vecptr(der);
+#endif
+ cert = std::shared_ptr<X509>(d2i_X509(nullptr, &p, der.size()), X509_free);
+ if (!cert) {
+// SWIFT_LOG(warning) << "Error creating certificate from DER data";
+ }
+ parse();
+}
+
+void OpenSSLCertificate::incrementReferenceCount() const {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ X509_up_ref(cert.get());
+#else
+ CRYPTO_add(&(cert.get()->references), 1, CRYPTO_LOCK_EVP_PKEY);
#endif
- cert = boost::shared_ptr<X509>(d2i_X509(NULL, &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));
- unsigned char* p = vecptr(result);
- i2d_X509(cert.get(), &p);
- return result;
+ ByteArray result;
+ if (!cert) {
+ return result;
+ }
+ 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);
+ 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);
- }
- }
+ // 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);
- if(subjectAltNameLoc != -1) {
- X509_EXTENSION* extension = X509_get_ext(cert.get(), subjectAltNameLoc);
- boost::shared_ptr<GENERAL_NAMES> generalNames(reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(extension)), GENERAL_NAMES_free);
- boost::shared_ptr<ASN1_OBJECT> xmppAddrObject(OBJ_txt2obj(ID_ON_XMPPADDR_OID, 1), ASN1_OBJECT_free);
- boost::shared_ptr<ASN1_OBJECT> dnsSRVObject(OBJ_txt2obj(ID_ON_DNSSRV_OID, 1), ASN1_OBJECT_free);
- for (int i = 0; i < sk_GENERAL_NAME_num(generalNames.get()); ++i) {
- GENERAL_NAME* generalName = sk_GENERAL_NAME_value(generalNames.get(), i);
- if (generalName->type == GEN_OTHERNAME) {
- OTHERNAME* otherName = generalName->d.otherName;
- if (OBJ_cmp(otherName->type_id, xmppAddrObject.get()) == 0) {
- // XmppAddr
- if (otherName->value->type != V_ASN1_UTF8STRING) {
- continue;
- }
- ASN1_UTF8STRING* xmppAddrValue = otherName->value->value.utf8string;
- addXMPPAddress(byteArrayToString(createByteArray(reinterpret_cast<const char*>(ASN1_STRING_data(xmppAddrValue)), ASN1_STRING_length(xmppAddrValue))));
- }
- else if (OBJ_cmp(otherName->type_id, dnsSRVObject.get()) == 0) {
- // SRVName
- if (otherName->value->type != V_ASN1_IA5STRING) {
- continue;
- }
- ASN1_IA5STRING* srvNameValue = otherName->value->value.ia5string;
- addSRVName(byteArrayToString(createByteArray(reinterpret_cast<const char*>(ASN1_STRING_data(srvNameValue)), ASN1_STRING_length(srvNameValue))));
- }
- }
- else if (generalName->type == GEN_DNS) {
- // DNSName
- addDNSName(byteArrayToString(createByteArray(ASN1_STRING_data(generalName->d.dNSName), ASN1_STRING_length(generalName->d.dNSName))));
- }
- }
- }
+ // subjectAltNames
+ int subjectAltNameLoc = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1);
+ if(subjectAltNameLoc != -1) {
+ X509_EXTENSION* extension = X509_get_ext(cert.get(), subjectAltNameLoc);
+ std::shared_ptr<GENERAL_NAMES> generalNames(reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(extension)), GENERAL_NAMES_free);
+ std::shared_ptr<ASN1_OBJECT> xmppAddrObject(OBJ_txt2obj(ID_ON_XMPPADDR_OID, 1), ASN1_OBJECT_free);
+ std::shared_ptr<ASN1_OBJECT> dnsSRVObject(OBJ_txt2obj(ID_ON_DNSSRV_OID, 1), ASN1_OBJECT_free);
+ for (int i = 0; i < sk_GENERAL_NAME_num(generalNames.get()); ++i) {
+ GENERAL_NAME* generalName = sk_GENERAL_NAME_value(generalNames.get(), i);
+ if (generalName->type == GEN_OTHERNAME) {
+ OTHERNAME* otherName = generalName->d.otherName;
+ if (OBJ_cmp(otherName->type_id, xmppAddrObject.get()) == 0) {
+ // XmppAddr
+ if (otherName->value->type != V_ASN1_UTF8STRING) {
+ continue;
+ }
+ ASN1_UTF8STRING* xmppAddrValue = otherName->value->value.utf8string;
+ addXMPPAddress(byteArrayToString(createByteArray(reinterpret_cast<const char*>(ASN1_STRING_data(xmppAddrValue)), ASN1_STRING_length(xmppAddrValue))));
+ }
+ else if (OBJ_cmp(otherName->type_id, dnsSRVObject.get()) == 0) {
+ // SRVName
+ if (otherName->value->type != V_ASN1_IA5STRING) {
+ continue;
+ }
+ ASN1_IA5STRING* srvNameValue = otherName->value->value.ia5string;
+ addSRVName(byteArrayToString(createByteArray(reinterpret_cast<const char*>(ASN1_STRING_data(srvNameValue)), ASN1_STRING_length(srvNameValue))));
+ }
+ }
+ else if (generalName->type == GEN_DNS) {
+ // DNSName
+ addDNSName(byteArrayToString(createByteArray(ASN1_STRING_data(generalName->d.dNSName), ASN1_STRING_length(generalName->d.dNSName))));
+ }
+ }
+ }
}
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificate.h b/Swiften/TLS/OpenSSL/OpenSSLCertificate.h
index 897b432..64da82a 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLCertificate.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLCertificate.h
@@ -1,70 +1,73 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <boost/shared_ptr.hpp>
+#include <memory>
+#include <string>
+
#include <openssl/ssl.h>
-#include <string>
#include <Swiften/TLS/Certificate.h>
namespace Swift {
- class OpenSSLCertificate : public Certificate {
- public:
- OpenSSLCertificate(boost::shared_ptr<X509>);
- OpenSSLCertificate(const ByteArray& der);
-
- std::string getSubjectName() const {
- return subjectName;
- }
-
- std::vector<std::string> getCommonNames() const {
- return commonNames;
- }
-
- std::vector<std::string> getSRVNames() const {
- return srvNames;
- }
-
- std::vector<std::string> getDNSNames() const {
- return dnsNames;
- }
-
- std::vector<std::string> getXMPPAddresses() const {
- return xmppAddresses;
- }
-
- ByteArray toDER() const;
-
- boost::shared_ptr<X509> getInternalX509() const {
- return cert;
- }
-
- private:
- void parse();
-
- void addSRVName(const std::string& name) {
- srvNames.push_back(name);
- }
-
- void addDNSName(const std::string& name) {
- dnsNames.push_back(name);
- }
-
- void addXMPPAddress(const std::string& addr) {
- xmppAddresses.push_back(addr);
- }
-
- private:
- boost::shared_ptr<X509> cert;
- std::string subjectName;
- std::vector<std::string> commonNames;
- std::vector<std::string> dnsNames;
- std::vector<std::string> xmppAddresses;
- std::vector<std::string> srvNames;
- };
+ class OpenSSLCertificate : public Certificate {
+ public:
+ OpenSSLCertificate(std::shared_ptr<X509>);
+ OpenSSLCertificate(const ByteArray& der);
+
+ std::string getSubjectName() const {
+ return subjectName;
+ }
+
+ std::vector<std::string> getCommonNames() const {
+ return commonNames;
+ }
+
+ std::vector<std::string> getSRVNames() const {
+ return srvNames;
+ }
+
+ std::vector<std::string> getDNSNames() const {
+ return dnsNames;
+ }
+
+ std::vector<std::string> getXMPPAddresses() const {
+ return xmppAddresses;
+ }
+
+ ByteArray toDER() const;
+
+ std::shared_ptr<X509> getInternalX509() const {
+ return cert;
+ }
+
+ void incrementReferenceCount() const;
+
+ private:
+ void parse();
+
+ void addSRVName(const std::string& name) {
+ srvNames.push_back(name);
+ }
+
+ void addDNSName(const std::string& name) {
+ dnsNames.push_back(name);
+ }
+
+ void addXMPPAddress(const std::string& addr) {
+ xmppAddresses.push_back(addr);
+ }
+
+ private:
+ std::shared_ptr<X509> cert;
+ std::string subjectName;
+ std::vector<std::string> commonNames;
+ std::vector<std::string> dnsNames;
+ std::vector<std::string> xmppAddresses;
+ std::vector<std::string> srvNames;
+ };
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp
new file mode 100644
index 0000000..73058a5
--- /dev/null
+++ b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h>
+
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+namespace Swift {
+
+OpenSSLCertificateFactory::OpenSSLCertificateFactory() {
+}
+
+OpenSSLCertificateFactory::~OpenSSLCertificateFactory() {
+}
+
+Certificate* OpenSSLCertificateFactory::createCertificateFromDER(const ByteArray& der) {
+ return new OpenSSLCertificate(der);
+}
+
+std::vector<std::shared_ptr<Certificate>> OpenSSLCertificateFactory::createCertificateChain(const ByteArray& data) {
+ std::vector<std::shared_ptr<Certificate>> certificateChain;
+
+ if (data.size() > std::numeric_limits<int>::max()) {
+ return certificateChain;
+ }
+
+ auto bio = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free);
+ BIO_write(bio.get(), vecptr(data), int(data.size()));
+
+ // Attempt parsing data as PEM
+ X509* openSSLCert = nullptr;
+ auto x509certFromPEM = PEM_read_bio_X509(bio.get(), &openSSLCert, nullptr, nullptr);
+ if (x509certFromPEM && openSSLCert) {
+ std::shared_ptr<X509> x509Cert(openSSLCert, X509_free);
+ certificateChain.emplace_back(std::make_shared<OpenSSLCertificate>(x509Cert));
+ openSSLCert = nullptr;
+ while ((x509certFromPEM = PEM_read_bio_X509(bio.get(), &openSSLCert, nullptr, nullptr)) != nullptr) {
+ std::shared_ptr<X509> x509Cert(openSSLCert, X509_free);
+ certificateChain.emplace_back(std::make_shared<OpenSSLCertificate>(x509Cert));
+ openSSLCert = nullptr;
+ }
+ }
+
+ // Clear any (expected) errors which resulted from PEM parsing
+ // If we don't do this, any existing TLS context will detect these
+ // spurious errors and fail to work
+ ERR_clear_error();
+
+ return certificateChain;
+}
+
+}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h
index 52f134c..a6974c8 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
@@ -10,10 +10,12 @@
#include <Swiften/TLS/OpenSSL/OpenSSLCertificate.h>
namespace Swift {
- class OpenSSLCertificateFactory : public CertificateFactory {
- public:
- virtual Certificate::ref createCertificateFromDER(const ByteArray& der) {
- return Certificate::ref(new OpenSSLCertificate(der));
- }
- };
+ class OpenSSLCertificateFactory : public CertificateFactory {
+ public:
+ OpenSSLCertificateFactory();
+ virtual ~OpenSSLCertificateFactory() override final;
+
+ virtual Certificate* createCertificateFromDER(const ByteArray& der) override final;
+ virtual std::vector<std::shared_ptr<Certificate>> createCertificateChain(const ByteArray& data) override final;
+ };
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
index 77f780f..86b0504 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
@@ -1,8 +1,9 @@
/*
- * Copyright (c) 2010-2013 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
+
#include <Swiften/Base/Platform.h>
#ifdef SWIFTEN_PLATFORM_WINDOWS
@@ -10,15 +11,21 @@
#include <wincrypt.h>
#endif
+#include <cassert>
+#include <memory>
#include <vector>
+
+
+#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
-#include <boost/smart_ptr/make_shared.hpp>
#if defined(SWIFTEN_PLATFORM_MACOSX)
#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>
@@ -35,300 +42,834 @@ namespace Swift {
static const int MAX_FINISHED_SIZE = 4096;
static const int SSL_READ_BUFFERSIZE = 8192;
+#define SSL_DEFAULT_VERIFY_DEPTH 5
+
+// Callback function declarations for certificate verification
+extern "C" {
+ static int certVerifyCallback(X509_STORE_CTX *store_ctx, void*);
+ static int verifyCallback(int preverify_ok, X509_STORE_CTX *ctx);
+}
+
static void freeX509Stack(STACK_OF(X509)* stack) {
- sk_X509_free(stack);
+ sk_X509_free(stack);
}
-OpenSSLContext::OpenSSLContext() : state_(Start), context_(0), handle_(0), readBIO_(0), writeBIO_(0) {
- ensureLibraryInitialized();
- context_ = SSL_CTX_new(TLSv1_client_method());
+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;
+ };
+
+ 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(const TLSOptions& options, Mode mode) : mode_(mode), state_(State::Start) {
+ ensureLibraryInitialized();
+ context_ = createSSL_CTX(mode_);
+ SSL_CTX_set_options(context_.get(), SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ if (mode_ == Mode::Server) {
+#if OPENSSL_VERSION_NUMBER < 0x1010
+ // Automatically select highest preference curve used for ECDH temporary keys used during
+ // key exchange if possible.
+ // Since version 1.1.0, this option is always enabled.
+ SSL_CTX_set_ecdh_auto(context_.get(), 1);
+#endif
+
+ SSL_CTX_set_tlsext_servername_arg(context_.get(), this);
+ SSL_CTX_set_tlsext_servername_callback(context_.get(), OpenSSLContext::handleServerNameCallback);
+ }
+
+ // TODO: implement CRL checking
+ // TODO: download CRL (HTTP transport)
+ // TODO: cache CRL downloads for configurable time period
- // 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
- // TODO: implement OCSP support
- // TODO: handle OCSP stapling see https://www.rfc-editor.org/rfc/rfc4366.txt
- // Load system certs
+ // Default for ignoreSystemTrustAnchors is false, i.e. load System TAs by default,
+ // to preserve previous behaviour
+ if (!options.ignoreSystemTrustAnchors) {
+ // Load system certs
#if defined(SWIFTEN_PLATFORM_WINDOWS)
- X509_STORE* store = SSL_CTX_get_cert_store(context_);
- 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());
- }
- }
- }
+ X509_STORE* store = SSL_CTX_get_cert_store(context_.get());
+ HCERTSTORE systemStore = CertOpenSystemStore(0, "ROOT");
+ if (systemStore) {
+ PCCERT_CONTEXT certContext = nullptr;
+ while (true) {
+ certContext = CertFindCertificateInStore(systemStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, 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_load_verify_locations(context_, NULL, "/etc/ssl/certs");
-#elif defined(SWIFTEN_PLATFORM_MACOSX)
- // 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_);
- 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);
- }
+ 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_.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
+ }
+ configure(options);
}
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::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_);
+}
+
+// This callback is called by OpenSSL when a client certificate needs to be verified.
+// In turn, this calls the verification callback which the user
+// of this OpenSSLContext has configured (if any).
+static int certVerifyCallback(X509_STORE_CTX* store_ctx, void* arg)
+{
+ OpenSSLContext* context = static_cast<OpenSSLContext *>(arg);
+
+ // Need to stash store_ctx pointer for use within verification
+ context->setX509StoreContext(store_ctx);
+
+ int ret;
+
+ // This callback shouldn't have been set up if the context doesn't
+ // have a verifyCertCallback set, but it doesn't hurt to double check
+ std::function<int (const TLSContext *)> cb = context->getVerifyCertCallback();
+ if (cb != nullptr) {
+ ret = cb(static_cast<const OpenSSLContext*>(context));
+ } else {
+ SWIFT_LOG(debug) << "certVerifyCallback called but context.verifyCertCallback is unset";
+ ret = 0;
+ }
+
+ context->setX509StoreContext(nullptr);
+ return ret;
+}
+
+// Convenience function to generate a text representation
+// of an X509 Name. This information is only used for logging.
+static std::string X509_NAME_to_text(X509_NAME* name)
+{
+ std::string nameString;
+
+ if (!name) {
+ return nameString;
+ }
+
+ std::unique_ptr<BIO, decltype(&BIO_free)> io(BIO_new(BIO_s_mem()), &BIO_free);
+ int r = X509_NAME_print_ex(io.get(), name, 0, XN_FLAG_RFC2253);
+ BIO_write(io.get(), "\0", 1);
+
+ if (r > 0) {
+ BUF_MEM* ptr = nullptr;
+ BIO_get_mem_ptr(io.get(), &ptr);
+ nameString = ptr->data;
+ }
+
+ return nameString;
+}
+
+// Check depth of certificate chain
+static int verifyCallback(int preverifyOk, X509_STORE_CTX* ctx)
+{
+ // Retrieve the pointer to the SSL of the connection currently treated
+ // and the application specific data stored into the SSL object.
+
+ int err = X509_STORE_CTX_get_error(ctx);
+ int depth = X509_STORE_CTX_get_error_depth(ctx);
+
+ SSL* ssl = static_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
+ SSL_CTX* sslctx = ssl ? SSL_get_SSL_CTX(ssl) : nullptr;
+ if (!sslctx) {
+ SWIFT_LOG(debug) << "verifyCallback: internal error";
+ return preverifyOk;
+ }
+
+ if (SSL_CTX_get_verify_mode(sslctx) == SSL_VERIFY_NONE) {
+ SWIFT_LOG(debug) << "verifyCallback: no verification required";
+ // No verification requested
+ return 1;
+ }
+
+ X509* errCert = X509_STORE_CTX_get_current_cert(ctx);
+ std::string subjectString;
+ if (errCert) {
+ X509_NAME* subjectName = X509_get_subject_name(errCert);
+ subjectString = X509_NAME_to_text(subjectName);
+ }
+
+ // Catch a too long certificate chain. The depth limit set using
+ // SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so
+ // that whenever the "depth>verify_depth" condition is met, we
+ // have violated the limit and want to log this error condition.
+ // We must do it here, because the CHAIN_TOO_LONG error would not
+ // be found explicitly; only errors introduced by cutting off the
+ // additional certificates would be logged.
+ if (depth >= SSL_CTX_get_verify_depth(sslctx)) {
+ preverifyOk = 0;
+ err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+ X509_STORE_CTX_set_error(ctx, err);
+ }
+
+ if (!preverifyOk) {
+ std::string issuerString;
+ if (errCert) {
+ X509_NAME* issuerName = X509_get_issuer_name(errCert);
+ issuerString = X509_NAME_to_text(issuerName);
+ }
+ SWIFT_LOG(debug) << "verifyCallback: verification error " <<
+ X509_verify_cert_error_string(err) << " depth: " <<
+ depth << " issuer: " << ((issuerString.length() > 0) ? issuerString : "<unknown>");
+ } else {
+ SWIFT_LOG(debug) << "verifyCallback: SSL depth: " << depth << " Subject: " <<
+ ((subjectString.length() > 0) ? subjectString : "<>");
+ }
+ // Always return "OK", as check on verification status
+ // will be performed once TLS handshake has completed,
+ // by calling OpenSSLContext::getVerificationErrorTypeForResult() to
+ // get the value set via X509_STORE_CTX_set_error() above.
+ return 1;
+}
+
+bool OpenSSLContext::configure(const TLSOptions &options)
+{
+ if (options.cipherSuites) {
+ std::string cipherSuites = *(options.cipherSuites);
+ if (SSL_CTX_set_cipher_list(context_.get(), cipherSuites.c_str()) != 1 ) {
+ SWIFT_LOG(debug) << "Failed to set cipher-suites";
+ return false;
+ }
+ }
+
+ if (options.context) {
+ const auto& contextId = *options.context;
+
+ if (SSL_CTX_set_session_id_context(context_.get(),
+ reinterpret_cast<const unsigned char *>(contextId.c_str()),
+ contextId.length()) != 1) {
+ SWIFT_LOG(debug) << "Failed to set context-id";
+ return false;
+ }
+ }
+
+ if (options.sessionCacheTimeout) {
+ int scto = *options.sessionCacheTimeout;
+ if (scto <= 0) {
+ SWIFT_LOG(debug) << "Invalid value for session-cache-timeout";
+ return false;
+ }
+ (void)SSL_CTX_set_timeout(context_.get(), scto);
+ if (SSL_CTX_get_timeout(context_.get()) != scto) {
+ SWIFT_LOG(debug) << "Failed to set session-cache-timeout";
+ return false;
+ }
+ }
+
+ if (options.verifyCertificateCallback) {
+ verifyCertCallback = *options.verifyCertificateCallback;
+ } else {
+ verifyCertCallback = nullptr;
+ }
+
+ if (options.verifyMode) {
+ TLSOptions::VerifyMode verify_mode = *options.verifyMode;
+ int mode;
+ switch (verify_mode) {
+ case TLSOptions::VerifyMode::None:
+ mode = SSL_VERIFY_NONE;
+ break;
+ case TLSOptions::VerifyMode::Required:
+ mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE;
+ break;
+ case TLSOptions::VerifyMode::Optional:
+ mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
+ break;
+ }
+
+ // Set up default certificate chain verification depth - may be overridden below
+ SSL_CTX_set_verify_depth(context_.get(), SSL_DEFAULT_VERIFY_DEPTH + 1);
+
+ // Set callbacks up
+ SSL_CTX_set_verify(context_.get(), mode, verifyCallback);
+
+ // Only set up certificate verification callback if a user callback has
+ // been configured via the TLSOptions
+ if (verifyCertCallback != nullptr) {
+ SSL_CTX_set_cert_verify_callback(context_.get(), certVerifyCallback, this);
+ }
+ }
+
+ if (options.verifyDepth) {
+ int depth = *options.verifyDepth;
+ if (depth <= 0) {
+ SWIFT_LOG(debug) << "Invalid value for verify-depth";
+ return false;
+ }
+
+ // Increase depth limit by one, so that verifyCallback() will log it
+ SSL_CTX_set_verify_depth(context_.get(), depth + 1);
+ }
+
+ auto updateOptionIfPresent = [this](boost::optional<bool> option, int flag) {
+ if (option) {
+ if (*option) {
+ SSL_CTX_set_options(context_.get(), flag);
+ }
+ else {
+ SSL_CTX_clear_options(context_.get(), flag);
+ }
+ }
+ };
+ updateOptionIfPresent(options.workaroundMicrosoftSessID, SSL_OP_MICROSOFT_SESS_ID_BUG);
+ updateOptionIfPresent(options.workaroundNetscapeChallenge, SSL_OP_NETSCAPE_CHALLENGE_BUG);
+ updateOptionIfPresent(options.workaroundNetscapeReuseCipherChange, SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG);
+ updateOptionIfPresent(options.workaroundSSLRef2ReuseCertType, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);
+ updateOptionIfPresent(options.workaroundMicrosoftBigSSLv3Buffer, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
+ updateOptionIfPresent(options.workaroundSSLeay080ClientDH, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);
+ updateOptionIfPresent(options.workaroundTLSD5, SSL_OP_TLS_D5_BUG);
+ updateOptionIfPresent(options.workaroundTLSBlockPadding, SSL_OP_TLS_BLOCK_PADDING_BUG);
+ updateOptionIfPresent(options.workaroundDontInsertEmptyFragments, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+ updateOptionIfPresent(options.workaroundAll, SSL_OP_ALL);
+ updateOptionIfPresent(options.suppressSSLv2, SSL_OP_NO_SSLv2);
+ updateOptionIfPresent(options.suppressSSLv3, SSL_OP_NO_SSLv3);
+ updateOptionIfPresent(options.suppressTLSv1, SSL_OP_NO_TLSv1);
+ updateOptionIfPresent(options.disableTLSRollBackBug, SSL_OP_TLS_ROLLBACK_BUG);
+ updateOptionIfPresent(options.singleDHUse, SSL_OP_SINGLE_DH_USE);
+
+ if (options.trustAnchors) {
+ // Add any additional Trust Anchors which are present in the TLSOptions
+ X509_STORE* store = SSL_CTX_get_cert_store(context_.get());
+
+ if (store) {
+ for (auto& certificate : *options.trustAnchors) {
+ auto openSSLCert = dynamic_cast<OpenSSLCertificate*>(certificate.get());
+ if (openSSLCert && openSSLCert->getInternalX509()) {
+ X509_STORE_add_cert(store, openSSLCert->getInternalX509().get());
+ // Don't need to increment reference count as X509_STORE_add_cert does thiS
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+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>(TLSError::AcceptFailed, openSSLInternalErrorToString()));
+ return;
+ }
+
+ initAndSetBIOs();
+
+ state_ = State::Accepting;
+ doAccept();
}
void OpenSSLContext::connect() {
- handle_ = SSL_new(context_);
- // Ownership of BIOs is ransferred
- readBIO_ = BIO_new(BIO_s_mem());
- writeBIO_ = BIO_new(BIO_s_mem());
- SSL_set_bio(handle_, readBIO_, writeBIO_);
-
- state_ = Connecting;
- doConnect();
+ connect(std::string());
+}
+
+void OpenSSLContext::connect(const std::string& requestedServerName) {
+ assert(mode_ == Mode::Client);
+ handle_ = std::unique_ptr<SSL>(SSL_new(context_.get()));
+ if (!handle_) {
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>(TLSError::ConnectFailed, openSSLInternalErrorToString()));
+ return;
+ }
+
+ if (!requestedServerName.empty()) {
+ if (SSL_set_tlsext_host_name(handle_.get(), const_cast<char*>(requestedServerName.c_str())) != 1) {
+ onError(std::make_shared<TLSError>(TLSError::ConnectFailed, "Failed to set Server Name Indication: " + openSSLInternalErrorToString()));\
+ return;
+ }
+ }
+
+ // 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();
+ sendPendingDataToApplication();
+ break;
+ }
+ case SSL_ERROR_WANT_READ:
+ sendPendingDataToNetwork();
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ sendPendingDataToNetwork();
+ break;
+ default:
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>(TLSError::AcceptFailed, openSSLInternalErrorToString()));
+ sendPendingDataToNetwork();
+ }
}
void OpenSSLContext::doConnect() {
- int connectResult = SSL_connect(handle_);
- int error = SSL_get_error(handle_, connectResult);
- switch (error) {
- case SSL_ERROR_NONE: {
- state_ = Connected;
- //std::cout << x->name << std::endl;
- //const char* comp = SSL_get_current_compression(handle_);
- //std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl;
- onConnected();
- break;
- }
- case SSL_ERROR_WANT_READ:
- sendPendingDataToNetwork();
- break;
- default:
- state_ = Error;
- onError(boost::make_shared<TLSError>());
- }
+ int connectResult = SSL_connect(handle_.get());
+ int error = SSL_get_error(handle_.get(), connectResult);
+ 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 is needed since OpenSSL 1.1.1 for the server to be able to calculate the
+ // TLS finish message.
+ sendPendingDataToNetwork();
+ break;
+ }
+ case SSL_ERROR_WANT_READ:
+ sendPendingDataToNetwork();
+ break;
+ default:
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>());
+ onError(std::make_shared<TLSError>(TLSError::ConnectFailed, openSSLInternalErrorToString()));
+ }
+}
+
+int OpenSSLContext::handleServerNameCallback(SSL* ssl, int*, void* arg) {
+ if (ssl == nullptr)
+ return SSL_TLSEXT_ERR_NOACK;
+
+ const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (servername) {
+ auto serverNameString = std::string(servername);
+ auto context = reinterpret_cast<OpenSSLContext*>(arg);
+ context->onServerNameRequested(serverNameString);
+
+ if (context->abortTLSHandshake_) {
+ context->abortTLSHandshake_ = false;
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ }
+ return SSL_TLSEXT_ERR_OK;
}
void OpenSSLContext::sendPendingDataToNetwork() {
- int size = BIO_pending(writeBIO_);
- if (size > 0) {
- SafeByteArray data;
- data.resize(size);
- BIO_read(writeBIO_, vecptr(data), size);
- onDataForNetwork(data);
- }
+ 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:
- doConnect();
- break;
- case Connected:
- sendPendingDataToApplication();
- break;
- case Start: assert(false); break;
- case Error: /*assert(false);*/ break;
- }
+ BIO_write(readBIO_, vecptr(data), data.size());
+ switch (state_) {
+ case State::Accepting:
+ doAccept();
+ break;
+ case State::Connecting:
+ doConnect();
+ break;
+ case State::Connected:
+ sendPendingDataToApplication();
+ 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) {
- sendPendingDataToNetwork();
- }
- else {
- state_ = Error;
- onError(boost::make_shared<TLSError>());
- }
+ auto ret = SSL_write(handle_.get(), vecptr(data), data.size());
+ if (ret > 0 || SSL_get_error(handle_.get(), ret) == SSL_ERROR_WANT_READ) {
+ sendPendingDataToNetwork();
+ }
+ else {
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>(TLSError::UnknownError, openSSLInternalErrorToString()));
+ }
}
void OpenSSLContext::sendPendingDataToApplication() {
- SafeByteArray data;
- data.resize(SSL_READ_BUFFERSIZE);
- int ret = SSL_read(handle_, 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());
- }
- if (ret < 0 && SSL_get_error(handle_, ret) != SSL_ERROR_WANT_READ) {
- state_ = Error;
- onError(boost::make_shared<TLSError>());
- }
+ SafeByteArray data;
+ data.resize(SSL_READ_BUFFERSIZE);
+ 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_.get(), vecptr(data), data.size());
+ }
+ if (ret < 0 && SSL_get_error(handle_.get(), ret) != SSL_ERROR_WANT_READ) {
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>(TLSError::UnknownError, openSSLInternalErrorToString()));
+ }
+}
+
+bool OpenSSLContext::setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& certificateChain) {
+ if (certificateChain.size() == 0) {
+ SWIFT_LOG(debug) << "Trying to load empty certificate chain.";
+ return false;
+ }
+
+ // load endpoint certificate
+ auto openSSLCert = dynamic_cast<OpenSSLCertificate*>(certificateChain[0].get());
+ if (!openSSLCert) {
+ return false;
+ }
+
+ // This increments the reference count on the X509 certificate automatically
+ if (SSL_CTX_use_certificate(context_.get(), openSSLCert->getInternalX509().get()) != 1) {
+ return false;
+ }
+
+ if (certificateChain.size() > 1) {
+ for (auto certificate = certificateChain.begin() + 1; certificate != certificateChain.end(); ++certificate) {
+ auto openSSLCert = dynamic_cast<OpenSSLCertificate*>(certificate->get());
+ if (!openSSLCert) {
+ return false;
+ }
+
+ if (SSL_CTX_add_extra_chain_cert(context_.get(), openSSLCert->getInternalX509().get()) != 1) {
+ SWIFT_LOG(debug) << "Trying to load empty certificate chain.";
+ return false;
+ }
+ // Have to manually increment reference count as SSL_CTX_add_extra_chain_cert does not do so
+ openSSLCert->incrementReferenceCount();
+ }
+ }
+
+ 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();
+ }
+ // Make sure resultKey is tidied up by wrapping it in a shared_ptr
+ auto resultKey = std::shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio.get(), nullptr, empty_or_preset_password_cb, password), EVP_PKEY_free);
+ if (resultKey) {
+ if (handle_) {
+ auto result = SSL_use_PrivateKey(handle_.get(), resultKey.get());
+ if (result != 1) {
+ return false;
+ }
+ }
+ else {
+ auto result = SSL_CTX_use_PrivateKey(context_.get(), resultKey.get());
+ if (result != 1) {
+ return false;
+ }
+ }
+ }
+ else {
+ return false;
+ }
+ return true;
+}
+
+void OpenSSLContext::setAbortTLSHandshake(bool abort) {
+ abortTLSHandshake_ = abort;
}
bool OpenSSLContext::setClientCertificate(CertificateWithKey::ref certificate) {
- boost::shared_ptr<PKCS12Certificate> pkcs12Certificate = boost::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());
- boost::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, NULL), PKCS12_free);
- BIO_free(bio);
- if (!pkcs12) {
- return false;
- }
-
- // Parse PKCS12
- X509 *certPtr = 0;
- EVP_PKEY* privateKeyPtr = 0;
- STACK_OF(X509)* caCertsPtr = 0;
- int result = PKCS12_parse(pkcs12.get(), reinterpret_cast<const char*>(vecptr(pkcs12Certificate->getPassword())), &privateKeyPtr, &certPtr, &caCertsPtr);
- if (result != 1) {
- return false;
- }
- boost::shared_ptr<X509> cert(certPtr, X509_free);
- boost::shared_ptr<EVP_PKEY> privateKey(privateKeyPtr, EVP_PKEY_free);
- boost::shared_ptr<STACK_OF(X509)> caCerts(caCertsPtr, freeX509Stack);
-
- // Use the key & certificates
- if (SSL_CTX_use_certificate(context_, cert.get()) != 1) {
- return false;
- }
- if (SSL_CTX_use_PrivateKey(context_, 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));
- }
- return true;
+ 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, nullptr), PKCS12_free);
+ BIO_free(bio);
+ if (!pkcs12) {
+ return false;
+ }
+
+ // Parse PKCS12
+ 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_.get(), cert.get()) != 1) {
+ return false;
+ }
+ 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_.get(), sk_X509_value(caCerts.get(), i));
+ }
+ return true;
+}
+
+bool OpenSSLContext::setDiffieHellmanParameters(const ByteArray& parametersInOpenSslDer) {
+ auto bio = std::unique_ptr<BIO, decltype(&BIO_free)>(BIO_new(BIO_s_mem()), BIO_free);
+ if (bio) {
+ BIO_write(bio.get(), vecptr(parametersInOpenSslDer), parametersInOpenSslDer.size());
+ auto result = 0L;
+ if (auto dhparams = d2i_DHparams_bio(bio.get(), nullptr)) {
+ if (handle_) {
+ result = SSL_set_tmp_dh(handle_.get(), dhparams);
+ }
+ else {
+ result = SSL_CTX_set_tmp_dh(context_.get(), dhparams);
+ }
+ DH_free(dhparams);
+ }
+ return result == 1;
+ }
+ return false;
}
std::vector<Certificate::ref> OpenSSLContext::getPeerCertificateChain() const {
- std::vector<Certificate::ref> result;
- STACK_OF(X509)* chain = SSL_get_peer_cert_chain(handle_);
- for (int i = 0; i < sk_X509_num(chain); ++i) {
- boost::shared_ptr<X509> x509Cert(X509_dup(sk_X509_value(chain, i)), X509_free);
-
- Certificate::ref cert = boost::make_shared<OpenSSLCertificate>(x509Cert);
- result.push_back(cert);
- }
- return result;
+ std::vector<Certificate::ref> result;
+
+ // When this context is a server, the peer (client) certificate
+ // is obtained via SSL_get_peer_certificate, and any other
+ // certificates set by the peer are available via SSL_get_peer_cert_chain.
+ // When this context is a client, all of the server's certificates are
+ // obtained using SSL_get_peer_cert_chain
+ if (mode_ == Mode::Server) {
+ auto cert = SSL_get_peer_certificate(handle_.get());
+ if (cert) {
+ // Do not need to copy the returned cert as SSL_get_peer_certificate
+ // increments the reference count on the certificate
+ std::shared_ptr<X509> x509Cert(cert, X509_free);
+ Certificate::ref cert = std::make_shared<OpenSSLCertificate>(x509Cert);
+ result.push_back(cert);
+ }
+ }
+
+ STACK_OF(X509)* chain = SSL_get_peer_cert_chain(handle_.get());
+ for (int i = 0; i < sk_X509_num(chain); ++i) {
+ // Here we do need to copy the returned cert, since SSL_get_peer_cert_chain
+ // does not increment the reference count on each certificate
+ 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;
}
-boost::shared_ptr<CertificateVerificationError> OpenSSLContext::getPeerCertificateVerificationError() const {
- int verifyResult = SSL_get_verify_result(handle_);
- if (verifyResult != X509_V_OK) {
- return boost::make_shared<CertificateVerificationError>(getVerificationErrorTypeForResult(verifyResult));
- }
- else {
- return boost::shared_ptr<CertificateVerificationError>();
- }
+std::shared_ptr<CertificateVerificationError> OpenSSLContext::getPeerCertificateVerificationError() const {
+ 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());
- data.resize(size);
- return data;
+ ByteArray data;
+ data.resize(MAX_FINISHED_SIZE);
+ auto size = SSL_get_finished(handle_.get(), vecptr(data), data.size());
+ data.resize(size);
+ return data;
}
+ByteArray OpenSSLContext::getPeerFinishMessage() const {
+ ByteArray data;
+ data.resize(MAX_FINISHED_SIZE);
+ auto size = SSL_get_peer_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;
-
- case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
- case X509_V_ERR_CERT_SIGNATURE_FAILURE:
- case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
- return CertificateVerificationError::InvalidSignature;
-
- case X509_V_ERR_INVALID_CA:
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
- case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
- return CertificateVerificationError::InvalidCA;
-
- case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
- case X509_V_ERR_AKID_SKID_MISMATCH:
- case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
- case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
- return CertificateVerificationError::UnknownError;
-
- // Unused / should not happen
- case X509_V_ERR_CERT_REVOKED:
- case X509_V_ERR_OUT_OF_MEM:
- case X509_V_ERR_UNABLE_TO_GET_CRL:
- case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
- case X509_V_ERR_CRL_SIGNATURE_FAILURE:
- case X509_V_ERR_CRL_NOT_YET_VALID:
- case X509_V_ERR_CRL_HAS_EXPIRED:
- case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
- case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
- case X509_V_ERR_CERT_CHAIN_TOO_LONG:
- case X509_V_ERR_APPLICATION_VERIFICATION:
- default:
- return CertificateVerificationError::UnknownError;
- }
+ 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;
+
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ return CertificateVerificationError::InvalidSignature;
+
+ case X509_V_ERR_INVALID_CA:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ return CertificateVerificationError::InvalidCA;
+
+ case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+ case X509_V_ERR_AKID_SKID_MISMATCH:
+ case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+ case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+ return CertificateVerificationError::UnknownError;
+
+ // Unused / should not happen
+ case X509_V_ERR_CERT_REVOKED:
+ case X509_V_ERR_OUT_OF_MEM:
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ case X509_V_ERR_APPLICATION_VERIFICATION:
+ default:
+ return CertificateVerificationError::UnknownError;
+ }
}
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h
index d4327ca..8eb5758 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h
@@ -1,53 +1,92 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <openssl/ssl.h>
-#include <Swiften/Base/boost_bsignals.h>
+#include <memory>
+
#include <boost/noncopyable.hpp>
+#include <boost/signals2.hpp>
+
+#include <openssl/ssl.h>
-#include <Swiften/TLS/TLSContext.h>
#include <Swiften/Base/ByteArray.h>
#include <Swiften/TLS/CertificateWithKey.h>
+#include <Swiften/TLS/TLSContext.h>
+#include <Swiften/TLS/TLSOptions.h>
+
+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(const TLSOptions& options, Mode mode);
+ virtual ~OpenSSLContext() override final;
- class OpenSSLContext : public TLSContext, boost::noncopyable {
- public:
- OpenSSLContext();
- ~OpenSSLContext();
+ void accept() override final;
+ void connect() override final;
+ void connect(const std::string& requestHostname) override final;
- void connect();
- bool setClientCertificate(CertificateWithKey::ref cert);
+ bool setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& certificateChain) override final;
+ bool setPrivateKey(const PrivateKey::ref& privateKey) override final;
+ bool setClientCertificate(CertificateWithKey::ref cert) override final;
+ void setAbortTLSHandshake(bool abort) override final;
+ bool setDiffieHellmanParameters(const ByteArray& parametersInOpenSslDer) 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;
- boost::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;
+ virtual ByteArray getPeerFinishMessage() const override final;
- private:
- static void ensureLibraryInitialized();
+ void setX509StoreContext(X509_STORE_CTX *ptr) { x509_store_ctx = ptr; }
+ std::function<int (const TLSContext *)> getVerifyCertCallback() { return verifyCertCallback; }
- static CertificateVerificationError::Type getVerificationErrorTypeForResult(int);
+ private:
+ bool configure(const TLSOptions& options);
+ static void ensureLibraryInitialized();
+ static int handleServerNameCallback(SSL *ssl, int *ad, void *arg);
+ static CertificateVerificationError::Type getVerificationErrorTypeForResult(int);
- void doConnect();
- void sendPendingDataToNetwork();
- void sendPendingDataToApplication();
+ void initAndSetBIOs();
+ void doAccept();
+ void doConnect();
+ void sendPendingDataToNetwork();
+ void sendPendingDataToApplication();
- private:
- enum State { Start, Connecting, Connected, Error };
+ private:
+ enum class State { Start, Accepting, Connecting, Connected, Error };
- State state_;
- SSL_CTX* context_;
- SSL* handle_;
- BIO* readBIO_;
- BIO* writeBIO_;
- };
+ const Mode mode_;
+ State state_;
+ std::unique_ptr<SSL_CTX> context_;
+ std::unique_ptr<SSL> handle_;
+ BIO* readBIO_ = nullptr;
+ BIO* writeBIO_ = nullptr;
+ bool abortTLSHandshake_ = false;
+ X509_STORE_CTX *x509_store_ctx = nullptr;
+ std::function<int (const TLSContext *)> verifyCertCallback = nullptr;
+ };
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp
index 671cba7..e332ca8 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp
@@ -1,28 +1,61 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <Swiften/TLS/OpenSSL/OpenSSLContextFactory.h>
-#include <Swiften/TLS/OpenSSL/OpenSSLContext.h>
+
+#include <openssl/bio.h>
+#include <openssl/dh.h>
+#include <openssl/pem.h>
+
#include <Swiften/Base/Log.h>
+#include <Swiften/TLS/OpenSSL/OpenSSLContext.h>
+
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
namespace Swift {
bool OpenSSLContextFactory::canCreate() const {
- return true;
+ return true;
}
-TLSContext* OpenSSLContextFactory::createTLSContext() {
- return new OpenSSLContext();
+std::unique_ptr<TLSContext> OpenSSLContextFactory::createTLSContext(const TLSOptions& options, TLSContext::Mode mode) {
+ return std::make_unique<OpenSSLContext>(options, mode);
+}
+
+ByteArray OpenSSLContextFactory::convertDHParametersFromPEMToDER(const std::string& dhParametersInPEM) {
+ ByteArray dhParametersInDER;
+
+ auto bio = std::unique_ptr<BIO, decltype(&BIO_free)>(BIO_new(BIO_s_mem()), BIO_free);
+ if (bio) {
+ BIO_write(bio.get(), dhParametersInPEM.data(), dhParametersInPEM.size());
+ if (auto params = PEM_read_bio_DHparams(bio.get(), nullptr, nullptr, nullptr)) {
+ unsigned char* buffer = nullptr;
+ auto len = i2d_DHparams(params, &buffer);
+ if (len > 0) {
+ dhParametersInDER = createByteArray(buffer, static_cast<size_t>(len));
+ free(buffer);
+ }
+ DH_free(params);
+
+ }
+ }
+ return dhParametersInDER;
}
void OpenSSLContextFactory::setCheckCertificateRevocation(bool check) {
- if (check) {
- assert(false);
- SWIFT_LOG(warning) << "CRL Checking not supported for OpenSSL" << std::endl;
- }
+ if (check) {
+ SWIFT_LOG(warning) << "CRL Checking not supported for OpenSSL";
+ assert(false);
+ }
+}
+
+void OpenSSLContextFactory::setDisconnectOnCardRemoval(bool check) {
+ if (check) {
+ SWIFT_LOG(warning) << "Smart cards not supported for OpenSSL";
+ }
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h
index 43ab960..834e479 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h
@@ -1,22 +1,25 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <Swiften/TLS/TLSContextFactory.h>
+#include <memory>
-#include <cassert>
+#include <Swiften/TLS/TLSContextFactory.h>
namespace Swift {
- class OpenSSLContextFactory : public TLSContextFactory {
- public:
- bool canCreate() const;
- virtual TLSContext* createTLSContext();
+ class OpenSSLContextFactory : public TLSContextFactory {
+ public:
+ bool canCreate() const override final;
+ virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode) override final;
+
+ virtual ByteArray convertDHParametersFromPEMToDER(const std::string& dhParametersInPEM) override final;
- // Not supported
- virtual void setCheckCertificateRevocation(bool b);
- };
+ // Not supported
+ virtual void setCheckCertificateRevocation(bool b) override final;
+ virtual void setDisconnectOnCardRemoval(bool b) override final;
+ };
}
diff --git a/Swiften/TLS/PKCS12Certificate.h b/Swiften/TLS/PKCS12Certificate.h
index 2d4c2e5..4ed5040 100644
--- a/Swiften/TLS/PKCS12Certificate.h
+++ b/Swiften/TLS/PKCS12Certificate.h
@@ -1,60 +1,62 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
+#include <boost/filesystem/path.hpp>
+
+#include <Swiften/Base/API.h>
#include <Swiften/Base/SafeByteArray.h>
#include <Swiften/TLS/CertificateWithKey.h>
-#include <boost/filesystem/path.hpp>
namespace Swift {
- class PKCS12Certificate : public Swift::CertificateWithKey {
- public:
- PKCS12Certificate() {}
+ class SWIFTEN_API PKCS12Certificate : public Swift::CertificateWithKey {
+ public:
+ PKCS12Certificate() {}
- PKCS12Certificate(const boost::filesystem::path& filename, const SafeByteArray& password) : password_(password) {
- readByteArrayFromFile(data_, filename);
- }
+ PKCS12Certificate(const boost::filesystem::path& filename, const SafeByteArray& password) : password_(password) {
+ readByteArrayFromFile(data_, filename);
+ }
- virtual ~PKCS12Certificate() {}
+ virtual ~PKCS12Certificate() {}
- virtual bool isNull() const {
- return data_.empty();
- }
+ virtual bool isNull() const {
+ return data_.empty();
+ }
- virtual bool isPrivateKeyExportable() const {
+ virtual bool isPrivateKeyExportable() const {
/////Hopefully a PKCS12 is never missing a private key
- return true;
- }
-
- virtual const std::string& getCertStoreName() const {
-///// assert(0);
- throw std::exception();
- }
-
- virtual const std::string& getCertName() const {
- /* We can return the original filename instead, if we care */
-///// assert(0);
- throw std::exception();
- }
-
- virtual const ByteArray& getData() const {
- return data_;
- }
-
- void setData(const ByteArray& data) {
- data_ = data;
- }
-
- virtual const SafeByteArray& getPassword() const {
- return password_;
- }
-
- private:
- ByteArray data_;
- SafeByteArray password_;
- };
+ return true;
+ }
+
+ virtual const std::string& getCertStoreName() const {
+///// assert(0);
+ throw std::exception();
+ }
+
+ virtual const std::string& getCertName() const {
+ /* We can return the original filename instead, if we care */
+///// assert(0);
+ throw std::exception();
+ }
+
+ virtual const ByteArray& getData() const {
+ return data_;
+ }
+
+ void setData(const ByteArray& data) {
+ data_ = data;
+ }
+
+ virtual const SafeByteArray& getPassword() const {
+ return password_;
+ }
+
+ private:
+ ByteArray data_;
+ SafeByteArray password_;
+ };
}
diff --git a/Swiften/TLS/PlatformTLSFactories.cpp b/Swiften/TLS/PlatformTLSFactories.cpp
index 64a5ab3..81f560b 100644
--- a/Swiften/TLS/PlatformTLSFactories.cpp
+++ b/Swiften/TLS/PlatformTLSFactories.cpp
@@ -1,48 +1,55 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
-#include <Swiften/Base/Platform.h>
#include <Swiften/TLS/PlatformTLSFactories.h>
-#include <cstring>
-#include <cassert>
-
+#include <Swiften/Base/Platform.h>
+#include <Swiften/TLS/CertificateFactory.h>
+#include <Swiften/TLS/TLSContextFactory.h>
#ifdef HAVE_OPENSSL
- #include "Swiften/TLS/OpenSSL/OpenSSLContextFactory.h"
- #include "Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h"
+ #include <Swiften/TLS/OpenSSL/OpenSSLContextFactory.h>
+ #include <Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h>
#endif
#ifdef HAVE_SCHANNEL
- #include "Swiften/TLS/Schannel/SchannelContextFactory.h"
- #include "Swiften/TLS/Schannel/SchannelCertificateFactory.h"
+ #include <Swiften/TLS/Schannel/SchannelContextFactory.h>
+ #include <Swiften/TLS/Schannel/SchannelCertificateFactory.h>
+#endif
+#ifdef HAVE_SECURETRANSPORT
+ #include <Swiften/TLS/SecureTransport/SecureTransportContextFactory.h>
+ #include <Swiften/TLS/SecureTransport/SecureTransportCertificateFactory.h>
#endif
namespace Swift {
-PlatformTLSFactories::PlatformTLSFactories() : contextFactory(NULL), certificateFactory(NULL) {
+PlatformTLSFactories::PlatformTLSFactories() : contextFactory(nullptr), certificateFactory(nullptr) {
#ifdef HAVE_OPENSSL
- contextFactory = new OpenSSLContextFactory();
- certificateFactory = new OpenSSLCertificateFactory();
+ contextFactory = new OpenSSLContextFactory();
+ certificateFactory = new OpenSSLCertificateFactory();
#endif
#ifdef HAVE_SCHANNEL
- contextFactory = new SchannelContextFactory();
- certificateFactory = new SchannelCertificateFactory();
+ contextFactory = new SchannelContextFactory();
+ certificateFactory = new SchannelCertificateFactory();
+#endif
+#ifdef HAVE_SECURETRANSPORT
+ contextFactory = new SecureTransportContextFactory();
+ certificateFactory = new SecureTransportCertificateFactory();
#endif
}
PlatformTLSFactories::~PlatformTLSFactories() {
- delete contextFactory;
- delete certificateFactory;
+ delete contextFactory;
+ delete certificateFactory;
}
TLSContextFactory* PlatformTLSFactories::getTLSContextFactory() const {
- return contextFactory;
+ return contextFactory;
}
CertificateFactory* PlatformTLSFactories::getCertificateFactory() const {
- return certificateFactory;
+ return certificateFactory;
}
}
diff --git a/Swiften/TLS/PlatformTLSFactories.h b/Swiften/TLS/PlatformTLSFactories.h
index 850d6f9..3821521 100644
--- a/Swiften/TLS/PlatformTLSFactories.h
+++ b/Swiften/TLS/PlatformTLSFactories.h
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
@@ -9,19 +9,19 @@
#include <Swiften/Base/API.h>
namespace Swift {
- class TLSContextFactory;
- class CertificateFactory;
+ class TLSContextFactory;
+ class CertificateFactory;
- class SWIFTEN_API PlatformTLSFactories {
- public:
- PlatformTLSFactories();
- ~PlatformTLSFactories();
+ class SWIFTEN_API PlatformTLSFactories {
+ public:
+ PlatformTLSFactories();
+ ~PlatformTLSFactories();
- TLSContextFactory* getTLSContextFactory() const;
- CertificateFactory* getCertificateFactory() const;
+ TLSContextFactory* getTLSContextFactory() const;
+ CertificateFactory* getCertificateFactory() const;
- private:
- TLSContextFactory* contextFactory;
- CertificateFactory* certificateFactory;
- };
+ private:
+ TLSContextFactory* contextFactory;
+ CertificateFactory* certificateFactory;
+ };
}
diff --git a/Swiften/TLS/PrivateKey.cpp b/Swiften/TLS/PrivateKey.cpp
new file mode 100644
index 0000000..31cac1d
--- /dev/null
+++ b/Swiften/TLS/PrivateKey.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/TLS/PrivateKey.h>
+
+namespace Swift {
+
+PrivateKey::PrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password) : data_(data), password_(password) {
+}
+
+const SafeByteArray& PrivateKey::getData() const {
+ return data_;
+}
+
+const boost::optional<SafeByteArray>& PrivateKey::getPassword() const {
+ return password_;
+}
+
+}
diff --git a/Swiften/TLS/PrivateKey.h b/Swiften/TLS/PrivateKey.h
new file mode 100644
index 0000000..332fc48
--- /dev/null
+++ b/Swiften/TLS/PrivateKey.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <boost/optional.hpp>
+
+#include <Swiften/Base/SafeByteArray.h>
+#include <Swiften/TLS/PrivateKey.h>
+
+namespace Swift {
+
+class PrivateKey {
+public:
+ using ref = std::shared_ptr<PrivateKey>;
+
+public:
+ PrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password = boost::optional<SafeByteArray>());
+
+ const SafeByteArray& getData() const;
+ const boost::optional<SafeByteArray>& getPassword() const;
+
+private:
+ SafeByteArray data_;
+ boost::optional<SafeByteArray> password_;
+};
+
+}
diff --git a/Swiften/TLS/SConscript b/Swiften/TLS/SConscript
index fb327b9..a79e6a1 100644
--- a/Swiften/TLS/SConscript
+++ b/Swiften/TLS/SConscript
@@ -1,35 +1,45 @@
Import("swiften_env")
objects = swiften_env.SwiftenObject([
- "Certificate.cpp",
- "CertificateFactory.cpp",
- "CertificateTrustChecker.cpp",
- "ServerIdentityVerifier.cpp",
- "TLSContext.cpp",
- "TLSContextFactory.cpp",
- ])
-
+ "Certificate.cpp",
+ "CertificateFactory.cpp",
+ "CertificateTrustChecker.cpp",
+ "PrivateKey.cpp",
+ "ServerIdentityVerifier.cpp",
+ "TLSContext.cpp",
+ "TLSContextFactory.cpp",
+ ])
+
myenv = swiften_env.Clone()
if myenv.get("HAVE_OPENSSL", 0) :
- myenv.MergeFlags(myenv["OPENSSL_FLAGS"])
- objects += myenv.SwiftenObject([
- "OpenSSL/OpenSSLContext.cpp",
- "OpenSSL/OpenSSLCertificate.cpp",
- "OpenSSL/OpenSSLContextFactory.cpp",
- ])
- myenv.Append(CPPDEFINES = "HAVE_OPENSSL")
+ myenv.MergeFlags(myenv["OPENSSL_FLAGS"])
+ objects += myenv.SwiftenObject([
+ "OpenSSL/OpenSSLContext.cpp",
+ "OpenSSL/OpenSSLCertificate.cpp",
+ "OpenSSL/OpenSSLContextFactory.cpp",
+ "OpenSSL/OpenSSLCertificateFactory.cpp",
+ ])
+ myenv.Append(CPPDEFINES = "HAVE_OPENSSL")
+ if myenv["PLATFORM"] == "win32" :
+ myenv.Append(CPPDEFINES = "NOMINMAX")
elif myenv.get("HAVE_SCHANNEL", 0) :
- swiften_env.Append(LIBS = ["Winscard"])
- objects += myenv.StaticObject([
- "CAPICertificate.cpp",
- "Schannel/SchannelContext.cpp",
- "Schannel/SchannelCertificate.cpp",
- "Schannel/SchannelContextFactory.cpp",
- ])
- myenv.Append(CPPDEFINES = "HAVE_SCHANNEL")
+ swiften_env.Append(LIBS = ["Winscard"])
+ objects += myenv.SwiftenObject([
+ "CAPICertificate.cpp",
+ "Schannel/SchannelContext.cpp",
+ "Schannel/SchannelCertificate.cpp",
+ "Schannel/SchannelContextFactory.cpp",
+ ])
+ myenv.Append(CPPDEFINES = "HAVE_SCHANNEL")
+elif myenv.get("HAVE_SECURETRANSPORT", 0) :
+ #swiften_env.Append(LIBS = ["Winscard"])
+ objects += myenv.SwiftenObject([
+ "SecureTransport/SecureTransportContext.mm",
+ "SecureTransport/SecureTransportCertificate.mm",
+ "SecureTransport/SecureTransportContextFactory.cpp",
+ ])
+ myenv.Append(CPPDEFINES = "HAVE_SECURETRANSPORT")
objects += myenv.SwiftenObject(["PlatformTLSFactories.cpp"])
-
-
swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/TLS/Schannel/SchannelCertificate.cpp b/Swiften/TLS/Schannel/SchannelCertificate.cpp
index 8aaec00..23c2479 100644
--- a/Swiften/TLS/Schannel/SchannelCertificate.cpp
+++ b/Swiften/TLS/Schannel/SchannelCertificate.cpp
@@ -4,6 +4,12 @@
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
#include "Swiften/TLS/Schannel/SchannelCertificate.h"
#include "Swiften/Base/ByteArray.h"
@@ -20,176 +26,176 @@ namespace Swift {
//------------------------------------------------------------------------
-SchannelCertificate::SchannelCertificate(const ScopedCertContext& certCtxt)
-: m_cert(certCtxt)
+SchannelCertificate::SchannelCertificate(const ScopedCertContext& certCtxt)
+: m_cert(certCtxt)
{
- parse();
+ parse();
}
//------------------------------------------------------------------------
SchannelCertificate::SchannelCertificate(const ByteArray& der)
{
- if (!der.empty())
- {
- // Convert the DER encoded certificate to a PCERT_CONTEXT
- CERT_BLOB certBlob = {0};
- certBlob.cbData = der.size();
- certBlob.pbData = (BYTE*)&der[0];
-
- if (!CryptQueryObject(
- CERT_QUERY_OBJECT_BLOB,
- &certBlob,
- CERT_QUERY_CONTENT_FLAG_CERT,
- CERT_QUERY_FORMAT_FLAG_ALL,
- 0,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- (const void**)m_cert.Reset()))
- {
- // TODO: Because Swiften isn't exception safe, we have no way to indicate failure
- }
- }
+ if (!der.empty())
+ {
+ // Convert the DER encoded certificate to a PCERT_CONTEXT
+ CERT_BLOB certBlob = {0};
+ certBlob.cbData = der.size();
+ certBlob.pbData = (BYTE*)&der[0];
+
+ if (!CryptQueryObject(
+ CERT_QUERY_OBJECT_BLOB,
+ &certBlob,
+ CERT_QUERY_CONTENT_FLAG_CERT,
+ CERT_QUERY_FORMAT_FLAG_ALL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ (const void**)m_cert.Reset()))
+ {
+ // TODO: Because Swiften isn't exception safe, we have no way to indicate failure
+ }
+ }
}
//------------------------------------------------------------------------
-ByteArray SchannelCertificate::toDER() const
+ByteArray SchannelCertificate::toDER() const
{
- ByteArray result;
+ ByteArray result;
+
+ // Serialize the certificate. The CERT_CONTEXT is already DER encoded.
+ result.resize(m_cert->cbCertEncoded);
+ memcpy(&result[0], m_cert->pbCertEncoded, result.size());
- // Serialize the certificate. The CERT_CONTEXT is already DER encoded.
- result.resize(m_cert->cbCertEncoded);
- memcpy(&result[0], m_cert->pbCertEncoded, result.size());
-
- return result;
+ return result;
}
//------------------------------------------------------------------------
std::string SchannelCertificate::wstrToStr(const std::wstring& wstr)
{
- if (wstr.empty())
- return "";
+ if (wstr.empty())
+ return "";
- // First request the size of the required UTF-8 buffer
- int numRequiredBytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);
- if (!numRequiredBytes)
- return "";
+ // First request the size of the required UTF-8 buffer
+ int numRequiredBytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);
+ if (!numRequiredBytes)
+ return "";
- // Allocate memory for the UTF-8 string
- std::vector<char> utf8Str(numRequiredBytes);
+ // Allocate memory for the UTF-8 string
+ std::vector<char> utf8Str(numRequiredBytes);
- int numConverted = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr.size(), &utf8Str[0], numRequiredBytes, NULL, NULL);
- if (!numConverted)
- return "";
+ int numConverted = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr.size(), &utf8Str[0], numRequiredBytes, NULL, NULL);
+ if (!numConverted)
+ return "";
- std::string str(&utf8Str[0], numConverted);
- return str;
+ std::string str(&utf8Str[0], numConverted);
+ return str;
}
//------------------------------------------------------------------------
-void SchannelCertificate::parse()
+void SchannelCertificate::parse()
{
- //
- // Subject name
- //
- DWORD requiredSize = CertNameToStr(X509_ASN_ENCODING, &m_cert->pCertInfo->Subject, CERT_OID_NAME_STR, NULL, 0);
- if (requiredSize > 1)
- {
- vector<char> rawSubjectName(requiredSize);
- CertNameToStr(X509_ASN_ENCODING, &m_cert->pCertInfo->Subject, CERT_OID_NAME_STR, &rawSubjectName[0], rawSubjectName.size());
- m_subjectName = std::string(&rawSubjectName[0]);
- }
-
- //
- // Common name
- //
- // Note: We only pull out one common name from the cert.
- requiredSize = CertGetNameString(m_cert, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, NULL, 0);
- if (requiredSize > 1)
- {
- vector<char> rawCommonName(requiredSize);
- requiredSize = CertGetNameString(m_cert, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, &rawCommonName[0], rawCommonName.size());
- m_commonNames.push_back( std::string(&rawCommonName[0]) );
- }
-
- //
- // Subject alternative names
- //
- PCERT_EXTENSION pExtensions = CertFindExtension(szOID_SUBJECT_ALT_NAME2, m_cert->pCertInfo->cExtension, m_cert->pCertInfo->rgExtension);
- if (pExtensions)
- {
- CRYPT_DECODE_PARA decodePara = {0};
- decodePara.cbSize = sizeof(decodePara);
-
- CERT_ALT_NAME_INFO* pAltNameInfo = NULL;
- DWORD altNameInfoSize = 0;
-
- BOOL status = CryptDecodeObjectEx(
- X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
- szOID_SUBJECT_ALT_NAME2,
- pExtensions->Value.pbData,
- pExtensions->Value.cbData,
- CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
- &decodePara,
- &pAltNameInfo,
- &altNameInfoSize);
-
- if (status && pAltNameInfo)
- {
- for (int i = 0; i < pAltNameInfo->cAltEntry; i++)
- {
- if (pAltNameInfo->rgAltEntry[i].dwAltNameChoice == CERT_ALT_NAME_DNS_NAME)
- addDNSName( wstrToStr( pAltNameInfo->rgAltEntry[i].pwszDNSName ) );
- }
- }
- }
-
- // if (pExtensions)
- // {
- // vector<wchar_t> subjectAlt
- // CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_SUBJECT_ALT_NAME, pExtensions->Value->pbData, pExtensions->Value->cbData, )
- // }
- //
- // // subjectAltNames
- // int subjectAltNameLoc = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1);
- // if(subjectAltNameLoc != -1) {
- // X509_EXTENSION* extension = X509_get_ext(cert.get(), subjectAltNameLoc);
- // boost::shared_ptr<GENERAL_NAMES> generalNames(reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(extension)), GENERAL_NAMES_free);
- // boost::shared_ptr<ASN1_OBJECT> xmppAddrObject(OBJ_txt2obj(ID_ON_XMPPADDR_OID, 1), ASN1_OBJECT_free);
- // boost::shared_ptr<ASN1_OBJECT> dnsSRVObject(OBJ_txt2obj(ID_ON_DNSSRV_OID, 1), ASN1_OBJECT_free);
- // for (int i = 0; i < sk_GENERAL_NAME_num(generalNames.get()); ++i) {
- // GENERAL_NAME* generalName = sk_GENERAL_NAME_value(generalNames.get(), i);
- // if (generalName->type == GEN_OTHERNAME) {
- // OTHERNAME* otherName = generalName->d.otherName;
- // if (OBJ_cmp(otherName->type_id, xmppAddrObject.get()) == 0) {
- // // XmppAddr
- // if (otherName->value->type != V_ASN1_UTF8STRING) {
- // continue;
- // }
- // ASN1_UTF8STRING* xmppAddrValue = otherName->value->value.utf8string;
- // addXMPPAddress(ByteArray(ASN1_STRING_data(xmppAddrValue), ASN1_STRING_length(xmppAddrValue)).toString());
- // }
- // else if (OBJ_cmp(otherName->type_id, dnsSRVObject.get()) == 0) {
- // // SRVName
- // if (otherName->value->type != V_ASN1_IA5STRING) {
- // continue;
- // }
- // ASN1_IA5STRING* srvNameValue = otherName->value->value.ia5string;
- // addSRVName(ByteArray(ASN1_STRING_data(srvNameValue), ASN1_STRING_length(srvNameValue)).toString());
- // }
- // }
- // else if (generalName->type == GEN_DNS) {
- // // DNSName
- // addDNSName(ByteArray(ASN1_STRING_data(generalName->d.dNSName), ASN1_STRING_length(generalName->d.dNSName)).toString());
- // }
- // }
- // }
+ //
+ // Subject name
+ //
+ DWORD requiredSize = CertNameToStr(X509_ASN_ENCODING, &m_cert->pCertInfo->Subject, CERT_OID_NAME_STR, NULL, 0);
+ if (requiredSize > 1)
+ {
+ vector<char> rawSubjectName(requiredSize);
+ CertNameToStr(X509_ASN_ENCODING, &m_cert->pCertInfo->Subject, CERT_OID_NAME_STR, &rawSubjectName[0], rawSubjectName.size());
+ m_subjectName = std::string(&rawSubjectName[0]);
+ }
+
+ //
+ // Common name
+ //
+ // Note: We only pull out one common name from the cert.
+ requiredSize = CertGetNameString(m_cert, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, NULL, 0);
+ if (requiredSize > 1)
+ {
+ vector<char> rawCommonName(requiredSize);
+ requiredSize = CertGetNameString(m_cert, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, &rawCommonName[0], rawCommonName.size());
+ m_commonNames.push_back( std::string(&rawCommonName[0]) );
+ }
+
+ //
+ // Subject alternative names
+ //
+ PCERT_EXTENSION pExtensions = CertFindExtension(szOID_SUBJECT_ALT_NAME2, m_cert->pCertInfo->cExtension, m_cert->pCertInfo->rgExtension);
+ if (pExtensions)
+ {
+ CRYPT_DECODE_PARA decodePara = {0};
+ decodePara.cbSize = sizeof(decodePara);
+
+ CERT_ALT_NAME_INFO* pAltNameInfo = NULL;
+ DWORD altNameInfoSize = 0;
+
+ BOOL status = CryptDecodeObjectEx(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ szOID_SUBJECT_ALT_NAME2,
+ pExtensions->Value.pbData,
+ pExtensions->Value.cbData,
+ CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
+ &decodePara,
+ &pAltNameInfo,
+ &altNameInfoSize);
+
+ if (status && pAltNameInfo)
+ {
+ for (int i = 0; i < pAltNameInfo->cAltEntry; i++)
+ {
+ if (pAltNameInfo->rgAltEntry[i].dwAltNameChoice == CERT_ALT_NAME_DNS_NAME)
+ addDNSName( wstrToStr( pAltNameInfo->rgAltEntry[i].pwszDNSName ) );
+ }
+ }
+ }
+
+ // if (pExtensions)
+ // {
+ // vector<wchar_t> subjectAlt
+ // CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_SUBJECT_ALT_NAME, pExtensions->Value->pbData, pExtensions->Value->cbData, )
+ // }
+ //
+ // // subjectAltNames
+ // int subjectAltNameLoc = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1);
+ // if(subjectAltNameLoc != -1) {
+ // X509_EXTENSION* extension = X509_get_ext(cert.get(), subjectAltNameLoc);
+ // std::shared_ptr<GENERAL_NAMES> generalNames(reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(extension)), GENERAL_NAMES_free);
+ // std::shared_ptr<ASN1_OBJECT> xmppAddrObject(OBJ_txt2obj(ID_ON_XMPPADDR_OID, 1), ASN1_OBJECT_free);
+ // std::shared_ptr<ASN1_OBJECT> dnsSRVObject(OBJ_txt2obj(ID_ON_DNSSRV_OID, 1), ASN1_OBJECT_free);
+ // for (int i = 0; i < sk_GENERAL_NAME_num(generalNames.get()); ++i) {
+ // GENERAL_NAME* generalName = sk_GENERAL_NAME_value(generalNames.get(), i);
+ // if (generalName->type == GEN_OTHERNAME) {
+ // OTHERNAME* otherName = generalName->d.otherName;
+ // if (OBJ_cmp(otherName->type_id, xmppAddrObject.get()) == 0) {
+ // // XmppAddr
+ // if (otherName->value->type != V_ASN1_UTF8STRING) {
+ // continue;
+ // }
+ // ASN1_UTF8STRING* xmppAddrValue = otherName->value->value.utf8string;
+ // addXMPPAddress(ByteArray(ASN1_STRING_data(xmppAddrValue), ASN1_STRING_length(xmppAddrValue)).toString());
+ // }
+ // else if (OBJ_cmp(otherName->type_id, dnsSRVObject.get()) == 0) {
+ // // SRVName
+ // if (otherName->value->type != V_ASN1_IA5STRING) {
+ // continue;
+ // }
+ // ASN1_IA5STRING* srvNameValue = otherName->value->value.ia5string;
+ // addSRVName(ByteArray(ASN1_STRING_data(srvNameValue), ASN1_STRING_length(srvNameValue)).toString());
+ // }
+ // }
+ // else if (generalName->type == GEN_DNS) {
+ // // DNSName
+ // addDNSName(ByteArray(ASN1_STRING_data(generalName->d.dNSName), ASN1_STRING_length(generalName->d.dNSName)).toString());
+ // }
+ // }
+ // }
}
//------------------------------------------------------------------------
diff --git a/Swiften/TLS/Schannel/SchannelCertificate.h b/Swiften/TLS/Schannel/SchannelCertificate.h
index 395d3ec..d3bd66c 100644
--- a/Swiften/TLS/Schannel/SchannelCertificate.h
+++ b/Swiften/TLS/Schannel/SchannelCertificate.h
@@ -4,83 +4,89 @@
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
#pragma once
-#include <boost/shared_ptr.hpp>
+#include <memory>
-#include "Swiften/Base/String.h"
-#include "Swiften/TLS/Certificate.h"
-#include "Swiften/TLS/Schannel/SchannelUtil.h"
+#include <Swiften/Base/String.h>
+#include <Swiften/TLS/Certificate.h>
+#include <Swiften/TLS/Schannel/SchannelUtil.h>
-namespace Swift
+namespace Swift
{
- class SchannelCertificate : public Certificate
- {
- public:
- typedef boost::shared_ptr<SchannelCertificate> ref;
-
- public:
- SchannelCertificate(const ScopedCertContext& certCtxt);
- SchannelCertificate(const ByteArray& der);
-
- std::string getSubjectName() const
- {
- return m_subjectName;
- }
-
- std::vector<std::string> getCommonNames() const
- {
- return m_commonNames;
- }
-
- std::vector<std::string> getSRVNames() const
- {
- return m_srvNames;
- }
-
- std::vector<std::string> getDNSNames() const
- {
- return m_dnsNames;
- }
-
- std::vector<std::string> getXMPPAddresses() const
- {
- return m_xmppAddresses;
- }
-
- ScopedCertContext getCertContext() const
- {
- return m_cert;
- }
-
- ByteArray toDER() const;
-
- private:
- void parse();
- std::string wstrToStr(const std::wstring& wstr);
-
- void addSRVName(const std::string& name)
- {
- m_srvNames.push_back(name);
- }
-
- void addDNSName(const std::string& name)
- {
- m_dnsNames.push_back(name);
- }
-
- void addXMPPAddress(const std::string& addr)
- {
- m_xmppAddresses.push_back(addr);
- }
-
- private:
- ScopedCertContext m_cert;
-
- std::string m_subjectName;
- std::vector<std::string> m_commonNames;
- std::vector<std::string> m_dnsNames;
- std::vector<std::string> m_xmppAddresses;
- std::vector<std::string> m_srvNames;
- };
+ class SchannelCertificate : public Certificate
+ {
+ public:
+ typedef std::shared_ptr<SchannelCertificate> ref;
+
+ public:
+ SchannelCertificate(const ScopedCertContext& certCtxt);
+ SchannelCertificate(const ByteArray& der);
+
+ std::string getSubjectName() const
+ {
+ return m_subjectName;
+ }
+
+ std::vector<std::string> getCommonNames() const
+ {
+ return m_commonNames;
+ }
+
+ std::vector<std::string> getSRVNames() const
+ {
+ return m_srvNames;
+ }
+
+ std::vector<std::string> getDNSNames() const
+ {
+ return m_dnsNames;
+ }
+
+ std::vector<std::string> getXMPPAddresses() const
+ {
+ return m_xmppAddresses;
+ }
+
+ ScopedCertContext getCertContext() const
+ {
+ return m_cert;
+ }
+
+ ByteArray toDER() const;
+
+ private:
+ void parse();
+ std::string wstrToStr(const std::wstring& wstr);
+
+ void addSRVName(const std::string& name)
+ {
+ m_srvNames.push_back(name);
+ }
+
+ void addDNSName(const std::string& name)
+ {
+ m_dnsNames.push_back(name);
+ }
+
+ void addXMPPAddress(const std::string& addr)
+ {
+ m_xmppAddresses.push_back(addr);
+ }
+
+ private:
+ ScopedCertContext m_cert;
+
+ std::string m_subjectName;
+ std::vector<std::string> m_commonNames;
+ std::vector<std::string> m_dnsNames;
+ std::vector<std::string> m_xmppAddresses;
+ std::vector<std::string> m_srvNames;
+ };
}
diff --git a/Swiften/TLS/Schannel/SchannelCertificateFactory.h b/Swiften/TLS/Schannel/SchannelCertificateFactory.h
index d09bb54..be97c52 100644
--- a/Swiften/TLS/Schannel/SchannelCertificateFactory.h
+++ b/Swiften/TLS/Schannel/SchannelCertificateFactory.h
@@ -10,10 +10,10 @@
#include <Swiften/TLS/Schannel/SchannelCertificate.h>
namespace Swift {
- class SchannelCertificateFactory : public CertificateFactory {
- public:
- virtual Certificate::ref createCertificateFromDER(const ByteArray& der) {
- return Certificate::ref(new SchannelCertificate(der));
- }
- };
+ class SchannelCertificateFactory : public CertificateFactory {
+ public:
+ virtual Certificate* createCertificateFromDER(const ByteArray& der) {
+ return new SchannelCertificate(der);
+ }
+ };
}
diff --git a/Swiften/TLS/Schannel/SchannelContext.cpp b/Swiften/TLS/Schannel/SchannelContext.cpp
index b4b2843..722fb4a 100644
--- a/Swiften/TLS/Schannel/SchannelContext.cpp
+++ b/Swiften/TLS/Schannel/SchannelContext.cpp
@@ -5,674 +5,703 @@
*/
/*
- * Copyright (c) 2012 Kevin Smith
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2012-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
+#include <Swiften/TLS/Schannel/SchannelContext.h>
+
#include <boost/bind.hpp>
-#include <Swiften/TLS/Schannel/SchannelContext.h>
-#include <Swiften/TLS/Schannel/SchannelCertificate.h>
-#include <Swiften/TLS/CAPICertificate.h>
#include <WinHTTP.h> /* For SECURITY_FLAG_IGNORE_CERT_CN_INVALID */
+#include <Swiften/Base/Log.h>
+#include <Swiften/TLS/CAPICertificate.h>
+#include <Swiften/TLS/Schannel/SchannelCertificate.h>
+
namespace Swift {
//------------------------------------------------------------------------
-SchannelContext::SchannelContext() : m_state(Start), m_secContext(0), m_my_cert_store(NULL), m_cert_store_name("MY"), m_cert_name(), m_smartcard_reader(), checkCertificateRevocation(true) {
- m_ctxtFlags = ISC_REQ_ALLOCATE_MEMORY |
- ISC_REQ_CONFIDENTIALITY |
- ISC_REQ_EXTENDED_ERROR |
- ISC_REQ_INTEGRITY |
- ISC_REQ_REPLAY_DETECT |
- ISC_REQ_SEQUENCE_DETECT |
- ISC_REQ_USE_SUPPLIED_CREDS |
- ISC_REQ_STREAM;
-
- ZeroMemory(&m_streamSizes, sizeof(m_streamSizes));
+SchannelContext::SchannelContext(bool tls1_0Workaround) : state_(Start), secContext_(0), myCertStore_(NULL), certStoreName_("MY"), certName_(), smartCardReader_(), checkCertificateRevocation_(true), tls1_0Workaround_(tls1_0Workaround), disconnectOnCardRemoval_(true) {
+ contextFlags_ = ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_INTEGRITY |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_USE_SUPPLIED_CREDS |
+ ISC_REQ_STREAM;
+
+ ZeroMemory(&streamSizes_, sizeof(streamSizes_));
}
//------------------------------------------------------------------------
SchannelContext::~SchannelContext() {
- if (m_my_cert_store) CertCloseStore(m_my_cert_store, 0);
+ SWIFT_LOG(debug) << "Destroying SchannelContext";
+ if (myCertStore_) {
+ if (CertCloseStore(myCertStore_, 0) == FALSE) {
+ SWIFT_LOG(debug) << "Failed to close the certificate store";
+ }
+ }
}
//------------------------------------------------------------------------
void SchannelContext::determineStreamSizes() {
- QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_STREAM_SIZES, &m_streamSizes);
+ if (QueryContextAttributes(contextHandle_, SECPKG_ATTR_STREAM_SIZES, &streamSizes_) != SEC_E_OK) {
+ SWIFT_LOG(debug) << "QueryContextAttributes failed to determinate the stream size";
+ }
}
//------------------------------------------------------------------------
void SchannelContext::connect() {
- ScopedCertContext pCertContext;
-
- m_state = Connecting;
-
- // If a user name is specified, then attempt to find a client
- // certificate. Otherwise, just create a NULL credential.
- if (!m_cert_name.empty()) {
- if (m_my_cert_store == NULL) {
- m_my_cert_store = CertOpenSystemStore(0, m_cert_store_name.c_str());
- if (!m_my_cert_store) {
- indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
- return;
- }
- }
-
- pCertContext = findCertificateInStore( m_my_cert_store, m_cert_name );
- if (pCertContext == NULL) {
- indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
- return;
- }
- }
-
- // We use an empty list for client certificates
- PCCERT_CONTEXT clientCerts[1] = {0};
-
- SCHANNEL_CRED sc = {0};
- sc.dwVersion = SCHANNEL_CRED_VERSION;
-
-/////SSL3?
- sc.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
- sc.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION;
-
- if (pCertContext) {
- sc.cCreds = 1;
- sc.paCred = pCertContext.GetPointer();
- sc.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
- }
- else {
- sc.cCreds = 0;
- sc.paCred = clientCerts;
- sc.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
- }
-
- // Swiften performs the server name check for us
- sc.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
-
- SECURITY_STATUS status = AcquireCredentialsHandle(
- NULL,
- UNISP_NAME,
- SECPKG_CRED_OUTBOUND,
- NULL,
- &sc,
- NULL,
- NULL,
- m_credHandle.Reset(),
- NULL);
-
- if (status != SEC_E_OK) {
- // We failed to obtain the credentials handle
- indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
- return;
- }
-
- SecBuffer outBuffers[2];
-
- // We let Schannel allocate the output buffer for us
- outBuffers[0].pvBuffer = NULL;
- outBuffers[0].cbBuffer = 0;
- outBuffers[0].BufferType = SECBUFFER_TOKEN;
-
- // Contains alert data if an alert is generated
- outBuffers[1].pvBuffer = NULL;
- outBuffers[1].cbBuffer = 0;
- outBuffers[1].BufferType = SECBUFFER_ALERT;
-
- // Make sure the output buffers are freed
- ScopedSecBuffer scopedOutputData(&outBuffers[0]);
- ScopedSecBuffer scopedOutputAlertData(&outBuffers[1]);
-
- SecBufferDesc outBufferDesc = {0};
- outBufferDesc.cBuffers = 2;
- outBufferDesc.pBuffers = outBuffers;
- outBufferDesc.ulVersion = SECBUFFER_VERSION;
-
- // Create the initial security context
- status = InitializeSecurityContext(
- m_credHandle,
- NULL,
- NULL,
- m_ctxtFlags,
- 0,
- 0,
- NULL,
- 0,
- m_ctxtHandle.Reset(),
- &outBufferDesc,
- &m_secContext,
- NULL);
-
- if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
- // We failed to initialize the security context
- handleCertError(status);
- indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
- return;
- }
-
- // Start the handshake
- sendDataOnNetwork(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer);
-
- if (status == SEC_E_OK) {
- status = validateServerCertificate();
- if (status != SEC_E_OK) {
- handleCertError(status);
- }
-
- m_state = Connected;
- determineStreamSizes();
-
- onConnected();
- }
+ ScopedCertContext pCertContext;
+
+ state_ = Connecting;
+
+ // If a user name is specified, then attempt to find a client
+ // certificate. Otherwise, just create a NULL credential.
+ if (!certName_.empty()) {
+ if (myCertStore_ == NULL) {
+ myCertStore_ = CertOpenSystemStore(0, certStoreName_.c_str());
+ if (!myCertStore_) {
+ indicateError(std::make_shared<TLSError>(TLSError::UnknownError));
+ return;
+ }
+ }
+
+ pCertContext = findCertificateInStore( myCertStore_, certName_ );
+ if (pCertContext == NULL) {
+ indicateError(std::make_shared<TLSError>(TLSError::UnknownError));
+ return;
+ }
+ }
+
+ // We use an empty list for client certificates
+ PCCERT_CONTEXT clientCerts[1] = {0};
+
+ SCHANNEL_CRED sc = {0};
+ sc.dwVersion = SCHANNEL_CRED_VERSION;
+
+ if (tls1_0Workaround_) {
+ sc.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;
+ }
+ else {
+ sc.grbitEnabledProtocols = /*SP_PROT_SSL3_CLIENT | */SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
+ }
+
+ sc.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION;
+
+ if (pCertContext) {
+ sc.cCreds = 1;
+ sc.paCred = pCertContext.GetPointer();
+ sc.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
+ }
+ else {
+ sc.cCreds = 0;
+ sc.paCred = clientCerts;
+ sc.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
+ }
+
+ // Swiften performs the server name check for us
+ sc.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
+
+ SECURITY_STATUS status = AcquireCredentialsHandle(
+ NULL,
+ UNISP_NAME,
+ SECPKG_CRED_OUTBOUND,
+ NULL,
+ &sc,
+ NULL,
+ NULL,
+ credHandle_.Reset(),
+ NULL);
+
+ if (status != SEC_E_OK) {
+ // We failed to obtain the credentials handle
+ indicateError(std::make_shared<TLSError>(TLSError::UnknownError));
+ return;
+ }
+
+ SecBuffer outBuffers[2];
+
+ // We let Schannel allocate the output buffer for us
+ outBuffers[0].pvBuffer = NULL;
+ outBuffers[0].cbBuffer = 0;
+ outBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ // Contains alert data if an alert is generated
+ outBuffers[1].pvBuffer = NULL;
+ outBuffers[1].cbBuffer = 0;
+ outBuffers[1].BufferType = SECBUFFER_ALERT;
+
+ // Make sure the output buffers are freed
+ ScopedSecBuffer scopedOutputData(&outBuffers[0]);
+ ScopedSecBuffer scopedOutputAlertData(&outBuffers[1]);
+
+ SecBufferDesc outBufferDesc = {0};
+ outBufferDesc.cBuffers = 2;
+ outBufferDesc.pBuffers = outBuffers;
+ outBufferDesc.ulVersion = SECBUFFER_VERSION;
+
+ // Create the initial security context
+ status = InitializeSecurityContext(
+ credHandle_,
+ NULL,
+ NULL,
+ contextFlags_,
+ 0,
+ 0,
+ NULL,
+ 0,
+ contextHandle_.Reset(),
+ &outBufferDesc,
+ &secContext_,
+ NULL);
+
+ if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
+ // We failed to initialize the security context
+ handleCertError(status);
+ indicateError(std::make_shared<TLSError>(TLSError::UnknownError));
+ return;
+ }
+
+ // Start the handshake
+ sendDataOnNetwork(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer);
+
+ if (status == SEC_E_OK) {
+ status = validateServerCertificate();
+ if (status != SEC_E_OK) {
+ handleCertError(status);
+ }
+
+ state_ = Connected;
+ determineStreamSizes();
+
+ onConnected();
+ }
}
//------------------------------------------------------------------------
SECURITY_STATUS SchannelContext::validateServerCertificate() {
- SchannelCertificate::ref pServerCert = boost::dynamic_pointer_cast<SchannelCertificate>( getPeerCertificate() );
- if (!pServerCert) {
- return SEC_E_WRONG_PRINCIPAL;
- }
-
- const LPSTR usage[] =
- {
- szOID_PKIX_KP_SERVER_AUTH,
- szOID_SERVER_GATED_CRYPTO,
- szOID_SGC_NETSCAPE
- };
-
- CERT_CHAIN_PARA chainParams = {0};
- chainParams.cbSize = sizeof(chainParams);
- chainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
- chainParams.RequestedUsage.Usage.cUsageIdentifier = ARRAYSIZE(usage);
- chainParams.RequestedUsage.Usage.rgpszUsageIdentifier = const_cast<LPSTR*>(usage);
-
- DWORD chainFlags = CERT_CHAIN_CACHE_END_CERT;
- if (checkCertificateRevocation) {
- chainFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
- }
-
- ScopedCertChainContext pChainContext;
-
- BOOL success = CertGetCertificateChain(
- NULL, // Use the chain engine for the current user (assumes a user is logged in)
- pServerCert->getCertContext(),
- NULL,
- pServerCert->getCertContext()->hCertStore,
- &chainParams,
- chainFlags,
- NULL,
- pChainContext.Reset());
-
- if (!success) {
- return GetLastError();
- }
-
- SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslChainPolicy = {0};
- sslChainPolicy.cbSize = sizeof(sslChainPolicy);
- sslChainPolicy.dwAuthType = AUTHTYPE_SERVER;
- sslChainPolicy.fdwChecks = SECURITY_FLAG_IGNORE_CERT_CN_INVALID; // Swiften checks the server name for us. Is this the correct way to disable server name checking?
- sslChainPolicy.pwszServerName = NULL;
-
- CERT_CHAIN_POLICY_PARA certChainPolicy = {0};
- certChainPolicy.cbSize = sizeof(certChainPolicy);
- certChainPolicy.dwFlags = CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG; // Swiften checks the server name for us. Is this the correct way to disable server name checking?
- certChainPolicy.pvExtraPolicyPara = &sslChainPolicy;
-
- CERT_CHAIN_POLICY_STATUS certChainPolicyStatus = {0};
- certChainPolicyStatus.cbSize = sizeof(certChainPolicyStatus);
-
- // Verify the chain
- if (!CertVerifyCertificateChainPolicy(
- CERT_CHAIN_POLICY_SSL,
- pChainContext,
- &certChainPolicy,
- &certChainPolicyStatus)) {
- return GetLastError();
- }
-
- if (certChainPolicyStatus.dwError != S_OK) {
- return certChainPolicyStatus.dwError;
- }
-
- return S_OK;
+ SchannelCertificate::ref pServerCert = std::dynamic_pointer_cast<SchannelCertificate>( getPeerCertificate() );
+ if (!pServerCert) {
+ return SEC_E_WRONG_PRINCIPAL;
+ }
+
+ const LPSTR usage[] =
+ {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+
+ CERT_CHAIN_PARA chainParams = {0};
+ chainParams.cbSize = sizeof(chainParams);
+ chainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ chainParams.RequestedUsage.Usage.cUsageIdentifier = ARRAYSIZE(usage);
+ chainParams.RequestedUsage.Usage.rgpszUsageIdentifier = const_cast<LPSTR*>(usage);
+
+ DWORD chainFlags = CERT_CHAIN_CACHE_END_CERT;
+ if (checkCertificateRevocation_) {
+ chainFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
+ }
+
+ ScopedCertChainContext pChainContext;
+
+ BOOL success = CertGetCertificateChain(
+ NULL, // Use the chain engine for the current user (assumes a user is logged in)
+ pServerCert->getCertContext(),
+ NULL,
+ pServerCert->getCertContext()->hCertStore,
+ &chainParams,
+ chainFlags,
+ NULL,
+ pChainContext.Reset());
+
+ if (!success) {
+ return GetLastError();
+ }
+
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslChainPolicy = {0};
+ sslChainPolicy.cbSize = sizeof(sslChainPolicy);
+ sslChainPolicy.dwAuthType = AUTHTYPE_SERVER;
+ sslChainPolicy.fdwChecks = SECURITY_FLAG_IGNORE_CERT_CN_INVALID; // Swiften checks the server name for us. Is this the correct way to disable server name checking?
+ sslChainPolicy.pwszServerName = NULL;
+
+ CERT_CHAIN_POLICY_PARA certChainPolicy = {0};
+ certChainPolicy.cbSize = sizeof(certChainPolicy);
+ certChainPolicy.dwFlags = CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG; // Swiften checks the server name for us. Is this the correct way to disable server name checking?
+ certChainPolicy.pvExtraPolicyPara = &sslChainPolicy;
+
+ CERT_CHAIN_POLICY_STATUS certChainPolicyStatus = {0};
+ certChainPolicyStatus.cbSize = sizeof(certChainPolicyStatus);
+
+ // Verify the chain
+ if (!CertVerifyCertificateChainPolicy(
+ CERT_CHAIN_POLICY_SSL,
+ pChainContext,
+ &certChainPolicy,
+ &certChainPolicyStatus)) {
+ return GetLastError();
+ }
+
+ if (certChainPolicyStatus.dwError != S_OK) {
+ return certChainPolicyStatus.dwError;
+ }
+
+ return S_OK;
}
//------------------------------------------------------------------------
void SchannelContext::appendNewData(const SafeByteArray& data) {
- size_t originalSize = m_receivedData.size();
- m_receivedData.resize(originalSize + data.size());
- memcpy(&m_receivedData[0] + originalSize, &data[0], data.size());
+ size_t originalSize = receivedData_.size();
+ receivedData_.resize(originalSize + data.size());
+ memcpy(&receivedData_[0] + originalSize, &data[0], data.size());
}
//------------------------------------------------------------------------
void SchannelContext::continueHandshake(const SafeByteArray& data) {
- appendNewData(data);
-
- while (!m_receivedData.empty()) {
- SecBuffer inBuffers[2];
-
- // Provide Schannel with the remote host's handshake data
- inBuffers[0].pvBuffer = (char*)(&m_receivedData[0]);
- inBuffers[0].cbBuffer = (unsigned long)m_receivedData.size();
- inBuffers[0].BufferType = SECBUFFER_TOKEN;
-
- inBuffers[1].pvBuffer = NULL;
- inBuffers[1].cbBuffer = 0;
- inBuffers[1].BufferType = SECBUFFER_EMPTY;
-
- SecBufferDesc inBufferDesc = {0};
- inBufferDesc.cBuffers = 2;
- inBufferDesc.pBuffers = inBuffers;
- inBufferDesc.ulVersion = SECBUFFER_VERSION;
-
- SecBuffer outBuffers[2];
-
- // We let Schannel allocate the output buffer for us
- outBuffers[0].pvBuffer = NULL;
- outBuffers[0].cbBuffer = 0;
- outBuffers[0].BufferType = SECBUFFER_TOKEN;
-
- // Contains alert data if an alert is generated
- outBuffers[1].pvBuffer = NULL;
- outBuffers[1].cbBuffer = 0;
- outBuffers[1].BufferType = SECBUFFER_ALERT;
-
- // Make sure the output buffers are freed
- ScopedSecBuffer scopedOutputData(&outBuffers[0]);
- ScopedSecBuffer scopedOutputAlertData(&outBuffers[1]);
-
- SecBufferDesc outBufferDesc = {0};
- outBufferDesc.cBuffers = 2;
- outBufferDesc.pBuffers = outBuffers;
- outBufferDesc.ulVersion = SECBUFFER_VERSION;
-
- SECURITY_STATUS status = InitializeSecurityContext(
- m_credHandle,
- m_ctxtHandle,
- NULL,
- m_ctxtFlags,
- 0,
- 0,
- &inBufferDesc,
- 0,
- NULL,
- &outBufferDesc,
- &m_secContext,
- NULL);
-
- if (status == SEC_E_INCOMPLETE_MESSAGE) {
- // Wait for more data to arrive
- break;
- }
- else if (status == SEC_I_CONTINUE_NEEDED) {
- SecBuffer* pDataBuffer = &outBuffers[0];
- SecBuffer* pExtraBuffer = &inBuffers[1];
-
- if (pDataBuffer && pDataBuffer->cbBuffer > 0 && pDataBuffer->pvBuffer != NULL) {
- sendDataOnNetwork(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer);
- }
-
- if (pExtraBuffer->BufferType == SECBUFFER_EXTRA) {
- m_receivedData.erase(m_receivedData.begin(), m_receivedData.end() - pExtraBuffer->cbBuffer);
- }
- else {
- m_receivedData.clear();
- }
-
- break;
- }
- else if (status == SEC_E_OK) {
- status = validateServerCertificate();
- if (status != SEC_E_OK) {
- handleCertError(status);
- }
-
- SecBuffer* pExtraBuffer = &inBuffers[1];
-
- if (pExtraBuffer && pExtraBuffer->cbBuffer > 0) {
- m_receivedData.erase(m_receivedData.begin(), m_receivedData.end() - pExtraBuffer->cbBuffer);
- }
- else {
- m_receivedData.clear();
- }
-
- m_state = Connected;
- determineStreamSizes();
-
- onConnected();
- }
- else {
- // We failed to initialize the security context
- handleCertError(status);
- indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
- return;
- }
- }
+ appendNewData(data);
+
+ while (!receivedData_.empty()) {
+ SecBuffer inBuffers[2];
+
+ // Provide Schannel with the remote host's handshake data
+ inBuffers[0].pvBuffer = static_cast<char*>(&receivedData_[0]);
+ inBuffers[0].cbBuffer = static_cast<unsigned long>(receivedData_.size());
+ inBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ inBuffers[1].pvBuffer = NULL;
+ inBuffers[1].cbBuffer = 0;
+ inBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+ SecBufferDesc inBufferDesc = {0};
+ inBufferDesc.cBuffers = 2;
+ inBufferDesc.pBuffers = inBuffers;
+ inBufferDesc.ulVersion = SECBUFFER_VERSION;
+
+ SecBuffer outBuffers[2];
+
+ // We let Schannel allocate the output buffer for us
+ outBuffers[0].pvBuffer = NULL;
+ outBuffers[0].cbBuffer = 0;
+ outBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ // Contains alert data if an alert is generated
+ outBuffers[1].pvBuffer = NULL;
+ outBuffers[1].cbBuffer = 0;
+ outBuffers[1].BufferType = SECBUFFER_ALERT;
+
+ // Make sure the output buffers are freed
+ ScopedSecBuffer scopedOutputData(&outBuffers[0]);
+ ScopedSecBuffer scopedOutputAlertData(&outBuffers[1]);
+
+ SecBufferDesc outBufferDesc = {0};
+ outBufferDesc.cBuffers = 2;
+ outBufferDesc.pBuffers = outBuffers;
+ outBufferDesc.ulVersion = SECBUFFER_VERSION;
+
+ SECURITY_STATUS status = InitializeSecurityContext(
+ credHandle_,
+ contextHandle_,
+ NULL,
+ contextFlags_,
+ 0,
+ 0,
+ &inBufferDesc,
+ 0,
+ NULL,
+ &outBufferDesc,
+ &secContext_,
+ NULL);
+
+ if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ // Wait for more data to arrive
+ break;
+ }
+ else if (status == SEC_I_CONTINUE_NEEDED) {
+ SecBuffer* pDataBuffer = &outBuffers[0];
+ SecBuffer* pExtraBuffer = &inBuffers[1];
+
+ if (pDataBuffer && pDataBuffer->cbBuffer > 0 && pDataBuffer->pvBuffer != NULL) {
+ sendDataOnNetwork(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer);
+ }
+
+ if (pExtraBuffer->BufferType == SECBUFFER_EXTRA) {
+ receivedData_.erase(receivedData_.begin(), receivedData_.end() - pExtraBuffer->cbBuffer);
+ }
+ else {
+ receivedData_.clear();
+ }
+
+ break;
+ }
+ else if (status == SEC_E_OK) {
+ status = validateServerCertificate();
+ if (status != SEC_E_OK) {
+ handleCertError(status);
+ }
+
+ SecBuffer* pExtraBuffer = &inBuffers[1];
+
+ if (pExtraBuffer && pExtraBuffer->cbBuffer > 0) {
+ receivedData_.erase(receivedData_.begin(), receivedData_.end() - pExtraBuffer->cbBuffer);
+ }
+ else {
+ receivedData_.clear();
+ }
+
+ state_ = Connected;
+ determineStreamSizes();
+
+ onConnected();
+ }
+ else {
+ // We failed to initialize the security context
+ handleCertError(status);
+ indicateError(std::make_shared<TLSError>(TLSError::UnknownError));
+ return;
+ }
+ }
}
//------------------------------------------------------------------------
-void SchannelContext::handleCertError(SECURITY_STATUS status)
+void SchannelContext::handleCertError(SECURITY_STATUS status)
{
- if (status == SEC_E_UNTRUSTED_ROOT ||
- status == CERT_E_UNTRUSTEDROOT ||
- status == CRYPT_E_ISSUER_SERIALNUMBER ||
- status == CRYPT_E_SIGNER_NOT_FOUND ||
- status == CRYPT_E_NO_TRUSTED_SIGNER) {
- m_verificationError = CertificateVerificationError::Untrusted;
- }
- else if (status == SEC_E_CERT_EXPIRED ||
- status == CERT_E_EXPIRED) {
- m_verificationError = CertificateVerificationError::Expired;
- }
- else if (status == CRYPT_E_SELF_SIGNED) {
- m_verificationError = CertificateVerificationError::SelfSigned;
- }
- else if (status == CRYPT_E_HASH_VALUE ||
- status == TRUST_E_CERT_SIGNATURE) {
- m_verificationError = CertificateVerificationError::InvalidSignature;
- }
- else if (status == CRYPT_E_REVOKED) {
- m_verificationError = CertificateVerificationError::Revoked;
- }
- else if (status == CRYPT_E_NO_REVOCATION_CHECK ||
- status == CRYPT_E_REVOCATION_OFFLINE) {
- m_verificationError = CertificateVerificationError::RevocationCheckFailed;
- }
- else {
- m_verificationError = CertificateVerificationError::UnknownError;
- }
+ if (status == SEC_E_UNTRUSTED_ROOT ||
+ status == CERT_E_UNTRUSTEDROOT ||
+ status == CRYPT_E_ISSUER_SERIALNUMBER ||
+ status == CRYPT_E_SIGNER_NOT_FOUND ||
+ status == CRYPT_E_NO_TRUSTED_SIGNER) {
+ verificationError_ = CertificateVerificationError::Untrusted;
+ }
+ else if (status == SEC_E_CERT_EXPIRED ||
+ status == CERT_E_EXPIRED) {
+ verificationError_ = CertificateVerificationError::Expired;
+ }
+ else if (status == CRYPT_E_SELF_SIGNED) {
+ verificationError_ = CertificateVerificationError::SelfSigned;
+ }
+ else if (status == CRYPT_E_HASH_VALUE ||
+ status == TRUST_E_CERT_SIGNATURE) {
+ verificationError_ = CertificateVerificationError::InvalidSignature;
+ }
+ else if (status == CRYPT_E_REVOKED) {
+ verificationError_ = CertificateVerificationError::Revoked;
+ }
+ else if (status == CRYPT_E_NO_REVOCATION_CHECK ||
+ status == CRYPT_E_REVOCATION_OFFLINE) {
+ verificationError_ = CertificateVerificationError::RevocationCheckFailed;
+ }
+ else if (status == CERT_E_WRONG_USAGE) {
+ verificationError_ = CertificateVerificationError::InvalidPurpose;
+ }
+ else {
+ verificationError_ = CertificateVerificationError::UnknownError;
+ }
}
//------------------------------------------------------------------------
void SchannelContext::sendDataOnNetwork(const void* pData, size_t dataSize) {
- if (dataSize > 0 && pData) {
- SafeByteArray byteArray(dataSize);
- memcpy(&byteArray[0], pData, dataSize);
+ if (dataSize > 0 && pData) {
+ SafeByteArray byteArray(dataSize);
+ memcpy(&byteArray[0], pData, dataSize);
- onDataForNetwork(byteArray);
- }
+ onDataForNetwork(byteArray);
+ }
}
//------------------------------------------------------------------------
void SchannelContext::forwardDataToApplication(const void* pData, size_t dataSize) {
- SafeByteArray byteArray(dataSize);
- memcpy(&byteArray[0], pData, dataSize);
+ SafeByteArray byteArray(dataSize);
+ memcpy(&byteArray[0], pData, dataSize);
- onDataForApplication(byteArray);
+ onDataForApplication(byteArray);
}
//------------------------------------------------------------------------
void SchannelContext::handleDataFromApplication(const SafeByteArray& data) {
- // Don't attempt to send data until we're fully connected
- if (m_state == Connecting) {
- return;
- }
+ // Don't attempt to send data until we're fully connected
+ if (state_ == Connecting) {
+ return;
+ }
- // Encrypt the data
- encryptAndSendData(data);
+ // Encrypt the data
+ encryptAndSendData(data);
}
//------------------------------------------------------------------------
void SchannelContext::handleDataFromNetwork(const SafeByteArray& data) {
- switch (m_state) {
- case Connecting:
- {
- // We're still establishing the connection, so continue the handshake
- continueHandshake(data);
- }
- break;
-
- case Connected:
- {
- // Decrypt the data
- decryptAndProcessData(data);
- }
- break;
-
- default:
- return;
- }
+ switch (state_) {
+ case Connecting:
+ {
+ // We're still establishing the connection, so continue the handshake
+ continueHandshake(data);
+ }
+ break;
+
+ case Connected:
+ {
+ // Decrypt the data
+ decryptAndProcessData(data);
+ }
+ break;
+
+ default:
+ return;
+ }
}
//------------------------------------------------------------------------
-void SchannelContext::indicateError(boost::shared_ptr<TLSError> error) {
- m_state = Error;
- m_receivedData.clear();
- onError(error);
+void SchannelContext::indicateError(std::shared_ptr<TLSError> error) {
+ state_ = Error;
+ receivedData_.clear();
+ onError(error);
}
//------------------------------------------------------------------------
void SchannelContext::decryptAndProcessData(const SafeByteArray& data) {
- SecBuffer inBuffers[4] = {0};
-
- appendNewData(data);
-
- while (!m_receivedData.empty()) {
- //
- // MSDN:
- // When using the Schannel SSP with contexts that are not connection oriented, on input,
- // the structure must contain four SecBuffer structures. Exactly one buffer must be of type
- // SECBUFFER_DATA and contain an encrypted message, which is decrypted in place. The remaining
- // buffers are used for output and must be of type SECBUFFER_EMPTY. For connection-oriented
- // contexts, a SECBUFFER_DATA type buffer must be supplied, as noted for nonconnection-oriented
- // contexts. Additionally, a second SECBUFFER_TOKEN type buffer that contains a security token
- // must also be supplied.
- //
- inBuffers[0].pvBuffer = (char*)(&m_receivedData[0]);
- inBuffers[0].cbBuffer = (unsigned long)m_receivedData.size();
- inBuffers[0].BufferType = SECBUFFER_DATA;
-
- inBuffers[1].BufferType = SECBUFFER_EMPTY;
- inBuffers[2].BufferType = SECBUFFER_EMPTY;
- inBuffers[3].BufferType = SECBUFFER_EMPTY;
-
- SecBufferDesc inBufferDesc = {0};
- inBufferDesc.cBuffers = 4;
- inBufferDesc.pBuffers = inBuffers;
- inBufferDesc.ulVersion = SECBUFFER_VERSION;
-
- size_t inData = m_receivedData.size();
- SECURITY_STATUS status = DecryptMessage(m_ctxtHandle, &inBufferDesc, 0, NULL);
-
- if (status == SEC_E_INCOMPLETE_MESSAGE) {
- // Wait for more data to arrive
- break;
- }
- else if (status == SEC_I_RENEGOTIATE) {
- // TODO: Handle renegotiation scenarios
- indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
- break;
- }
- else if (status == SEC_I_CONTEXT_EXPIRED) {
- indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
- break;
- }
- else if (status != SEC_E_OK) {
- indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
- break;
- }
-
- SecBuffer* pDataBuffer = NULL;
- SecBuffer* pExtraBuffer = NULL;
- for (int i = 0; i < 4; ++i) {
- if (inBuffers[i].BufferType == SECBUFFER_DATA) {
- pDataBuffer = &inBuffers[i];
- }
- else if (inBuffers[i].BufferType == SECBUFFER_EXTRA) {
- pExtraBuffer = &inBuffers[i];
- }
- }
-
- if (pDataBuffer && pDataBuffer->cbBuffer > 0 && pDataBuffer->pvBuffer != NULL) {
- forwardDataToApplication(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer);
- }
-
- // If there is extra data left over from the decryption operation, we call DecryptMessage() again
- if (pExtraBuffer) {
- m_receivedData.erase(m_receivedData.begin(), m_receivedData.end() - pExtraBuffer->cbBuffer);
- }
- else {
- // We're done
- m_receivedData.erase(m_receivedData.begin(), m_receivedData.begin() + inData);
- }
- }
+ SecBuffer inBuffers[4] = {0};
+
+ appendNewData(data);
+
+ while (!receivedData_.empty()) {
+ //
+ // MSDN:
+ // When using the Schannel SSP with contexts that are not connection oriented, on input,
+ // the structure must contain four SecBuffer structures. Exactly one buffer must be of type
+ // SECBUFFER_DATA and contain an encrypted message, which is decrypted in place. The remaining
+ // buffers are used for output and must be of type SECBUFFER_EMPTY. For connection-oriented
+ // contexts, a SECBUFFER_DATA type buffer must be supplied, as noted for nonconnection-oriented
+ // contexts. Additionally, a second SECBUFFER_TOKEN type buffer that contains a security token
+ // must also be supplied.
+ //
+ inBuffers[0].pvBuffer = static_cast<char*>(&receivedData_[0]);
+ inBuffers[0].cbBuffer = static_cast<unsigned long>(receivedData_.size());
+ inBuffers[0].BufferType = SECBUFFER_DATA;
+
+ inBuffers[1].BufferType = SECBUFFER_EMPTY;
+ inBuffers[2].BufferType = SECBUFFER_EMPTY;
+ inBuffers[3].BufferType = SECBUFFER_EMPTY;
+
+ SecBufferDesc inBufferDesc = {0};
+ inBufferDesc.cBuffers = 4;
+ inBufferDesc.pBuffers = inBuffers;
+ inBufferDesc.ulVersion = SECBUFFER_VERSION;
+
+ size_t inData = receivedData_.size();
+ SECURITY_STATUS status = DecryptMessage(contextHandle_, &inBufferDesc, 0, NULL);
+
+ if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ // Wait for more data to arrive
+ break;
+ }
+ else if (status == SEC_I_RENEGOTIATE) {
+ // TODO: Handle renegotiation scenarios
+ indicateError(std::make_shared<TLSError>(TLSError::UnknownError));
+ break;
+ }
+ else if (status == SEC_I_CONTEXT_EXPIRED) {
+ indicateError(std::make_shared<TLSError>(TLSError::UnknownError));
+ break;
+ }
+ else if (status != SEC_E_OK) {
+ indicateError(std::make_shared<TLSError>(TLSError::UnknownError));
+ break;
+ }
+
+ SecBuffer* pDataBuffer = NULL;
+ SecBuffer* pExtraBuffer = NULL;
+ for (int i = 0; i < 4; ++i) {
+ if (inBuffers[i].BufferType == SECBUFFER_DATA) {
+ pDataBuffer = &inBuffers[i];
+ }
+ else if (inBuffers[i].BufferType == SECBUFFER_EXTRA) {
+ pExtraBuffer = &inBuffers[i];
+ }
+ }
+
+ if (pDataBuffer && pDataBuffer->cbBuffer > 0 && pDataBuffer->pvBuffer != NULL) {
+ forwardDataToApplication(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer);
+ }
+
+ // If there is extra data left over from the decryption operation, we call DecryptMessage() again
+ if (pExtraBuffer) {
+ receivedData_.erase(receivedData_.begin(), receivedData_.end() - pExtraBuffer->cbBuffer);
+ }
+ else {
+ // We're done
+ receivedData_.erase(receivedData_.begin(), receivedData_.begin() + inData);
+ }
+ }
}
//------------------------------------------------------------------------
void SchannelContext::encryptAndSendData(const SafeByteArray& data) {
- if (m_streamSizes.cbMaximumMessage == 0) {
- return;
- }
-
- SecBuffer outBuffers[4] = {0};
-
- // Calculate the largest required size of the send buffer
- size_t messageBufferSize = (data.size() > m_streamSizes.cbMaximumMessage)
- ? m_streamSizes.cbMaximumMessage
- : data.size();
-
- // Allocate a packet for the encrypted data
- SafeByteArray sendBuffer;
- sendBuffer.resize(m_streamSizes.cbHeader + messageBufferSize + m_streamSizes.cbTrailer);
-
- size_t bytesSent = 0;
- do {
- size_t bytesLeftToSend = data.size() - bytesSent;
-
- // Calculate how much of the send buffer we'll be using for this chunk
- size_t bytesToSend = (bytesLeftToSend > m_streamSizes.cbMaximumMessage)
- ? m_streamSizes.cbMaximumMessage
- : bytesLeftToSend;
-
- // Copy the plain text data into the send buffer
- memcpy(&sendBuffer[0] + m_streamSizes.cbHeader, &data[0] + bytesSent, bytesToSend);
-
- outBuffers[0].pvBuffer = &sendBuffer[0];
- outBuffers[0].cbBuffer = m_streamSizes.cbHeader;
- outBuffers[0].BufferType = SECBUFFER_STREAM_HEADER;
-
- outBuffers[1].pvBuffer = &sendBuffer[0] + m_streamSizes.cbHeader;
- outBuffers[1].cbBuffer = (unsigned long)bytesToSend;
- outBuffers[1].BufferType = SECBUFFER_DATA;
-
- outBuffers[2].pvBuffer = &sendBuffer[0] + m_streamSizes.cbHeader + bytesToSend;
- outBuffers[2].cbBuffer = m_streamSizes.cbTrailer;
- outBuffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
-
- outBuffers[3].pvBuffer = 0;
- outBuffers[3].cbBuffer = 0;
- outBuffers[3].BufferType = SECBUFFER_EMPTY;
-
- SecBufferDesc outBufferDesc = {0};
- outBufferDesc.cBuffers = 4;
- outBufferDesc.pBuffers = outBuffers;
- outBufferDesc.ulVersion = SECBUFFER_VERSION;
-
- SECURITY_STATUS status = EncryptMessage(m_ctxtHandle, 0, &outBufferDesc, 0);
- if (status != SEC_E_OK) {
- indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
- return;
- }
-
- sendDataOnNetwork(&sendBuffer[0], outBuffers[0].cbBuffer + outBuffers[1].cbBuffer + outBuffers[2].cbBuffer);
- bytesSent += bytesToSend;
-
- } while (bytesSent < data.size());
+ if (streamSizes_.cbMaximumMessage == 0) {
+ return;
+ }
+
+ SecBuffer outBuffers[4] = {0};
+
+ // Calculate the largest required size of the send buffer
+ size_t messageBufferSize = (data.size() > streamSizes_.cbMaximumMessage)
+ ? streamSizes_.cbMaximumMessage
+ : data.size();
+
+ // Allocate a packet for the encrypted data
+ SafeByteArray sendBuffer;
+ sendBuffer.resize(streamSizes_.cbHeader + messageBufferSize + streamSizes_.cbTrailer);
+
+ size_t bytesSent = 0;
+ do {
+ size_t bytesLeftToSend = data.size() - bytesSent;
+
+ // Calculate how much of the send buffer we'll be using for this chunk
+ size_t bytesToSend = (bytesLeftToSend > streamSizes_.cbMaximumMessage)
+ ? streamSizes_.cbMaximumMessage
+ : bytesLeftToSend;
+
+ // Copy the plain text data into the send buffer
+ memcpy(&sendBuffer[0] + streamSizes_.cbHeader, &data[0] + bytesSent, bytesToSend);
+
+ outBuffers[0].pvBuffer = &sendBuffer[0];
+ outBuffers[0].cbBuffer = streamSizes_.cbHeader;
+ outBuffers[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+ outBuffers[1].pvBuffer = &sendBuffer[0] + streamSizes_.cbHeader;
+ outBuffers[1].cbBuffer = static_cast<unsigned long>(bytesToSend);
+ outBuffers[1].BufferType = SECBUFFER_DATA;
+
+ outBuffers[2].pvBuffer = &sendBuffer[0] + streamSizes_.cbHeader + bytesToSend;
+ outBuffers[2].cbBuffer = streamSizes_.cbTrailer;
+ outBuffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+ outBuffers[3].pvBuffer = 0;
+ outBuffers[3].cbBuffer = 0;
+ outBuffers[3].BufferType = SECBUFFER_EMPTY;
+
+ SecBufferDesc outBufferDesc = {0};
+ outBufferDesc.cBuffers = 4;
+ outBufferDesc.pBuffers = outBuffers;
+ outBufferDesc.ulVersion = SECBUFFER_VERSION;
+
+ SECURITY_STATUS status = EncryptMessage(contextHandle_, 0, &outBufferDesc, 0);
+ if (status != SEC_E_OK) {
+ indicateError(std::make_shared<TLSError>(TLSError::UnknownError));
+ return;
+ }
+
+ sendDataOnNetwork(&sendBuffer[0], outBuffers[0].cbBuffer + outBuffers[1].cbBuffer + outBuffers[2].cbBuffer);
+ bytesSent += bytesToSend;
+
+ } while (bytesSent < data.size());
}
//------------------------------------------------------------------------
bool SchannelContext::setClientCertificate(CertificateWithKey::ref certificate) {
- boost::shared_ptr<CAPICertificate> capiCertificate = boost::dynamic_pointer_cast<CAPICertificate>(certificate);
- if (!capiCertificate || capiCertificate->isNull()) {
- return false;
- }
+ std::shared_ptr<CAPICertificate> capiCertificate = std::dynamic_pointer_cast<CAPICertificate>(certificate);
+ if (!capiCertificate || capiCertificate->isNull()) {
+ return false;
+ }
- userCertificate = capiCertificate;
+ userCertificate_ = capiCertificate;
- // We assume that the Certificate Store Name/Certificate Name
- // are valid at this point
- m_cert_store_name = capiCertificate->getCertStoreName();
- m_cert_name = capiCertificate->getCertName();
+ // We assume that the Certificate Store Name/Certificate Name
+ // are valid at this point
+ certStoreName_ = capiCertificate->getCertStoreName();
+ certName_ = capiCertificate->getCertName();
////At the moment this is only useful for logging:
- m_smartcard_reader = capiCertificate->getSmartCardReaderName();
+ smartCardReader_ = capiCertificate->getSmartCardReaderName();
- capiCertificate->onCertificateCardRemoved.connect(boost::bind(&SchannelContext::handleCertificateCardRemoved, this));
+ capiCertificate->onCertificateCardRemoved.connect(boost::bind(&SchannelContext::handleCertificateCardRemoved, this));
- return true;
+ return true;
}
//------------------------------------------------------------------------
void SchannelContext::handleCertificateCardRemoved() {
- indicateError(boost::make_shared<TLSError>(TLSError::CertificateCardRemoved));
+ if (disconnectOnCardRemoval_) {
+ indicateError(std::make_shared<TLSError>(TLSError::CertificateCardRemoved));
+ }
}
//------------------------------------------------------------------------
std::vector<Certificate::ref> SchannelContext::getPeerCertificateChain() const {
- std::vector<Certificate::ref> certificateChain;
- ScopedCertContext pServerCert;
- ScopedCertContext pIssuerCert;
- ScopedCertContext pCurrentCert;
- SECURITY_STATUS status = QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset());
-
- if (status != SEC_E_OK) {
- return certificateChain;
- }
- certificateChain.push_back(boost::make_shared<SchannelCertificate>(pServerCert));
-
- pCurrentCert = pServerCert;
- while(pCurrentCert.GetPointer()) {
- DWORD dwVerificationFlags = 0;
- pIssuerCert = CertGetIssuerCertificateFromStore(pServerCert->hCertStore, pCurrentCert, NULL, &dwVerificationFlags );
- if (!(*pIssuerCert.GetPointer())) {
- break;
- }
- certificateChain.push_back(boost::make_shared<SchannelCertificate>(pIssuerCert));
-
- pCurrentCert = pIssuerCert;
- pIssuerCert = NULL;
- }
- return certificateChain;
+ std::vector<Certificate::ref> certificateChain;
+ ScopedCertContext pServerCert;
+ ScopedCertContext pIssuerCert;
+ ScopedCertContext pCurrentCert;
+ SECURITY_STATUS status = QueryContextAttributes(contextHandle_, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset());
+
+ if (status != SEC_E_OK) {
+ SWIFT_LOG(debug) << "Error while Querying the Certificate Chain";
+ return certificateChain;
+ }
+ certificateChain.push_back(std::make_shared<SchannelCertificate>(pServerCert));
+
+ pCurrentCert = pServerCert;
+ while(pCurrentCert.GetPointer()) {
+ DWORD dwVerificationFlags = 0;
+ pIssuerCert = CertGetIssuerCertificateFromStore(pServerCert->hCertStore, pCurrentCert, NULL, &dwVerificationFlags );
+ if (!(*pIssuerCert.GetPointer())) {
+ break;
+ }
+ certificateChain.push_back(std::make_shared<SchannelCertificate>(pIssuerCert));
+
+ pCurrentCert = pIssuerCert;
+ pIssuerCert = NULL;
+ }
+ return certificateChain;
}
//------------------------------------------------------------------------
CertificateVerificationError::ref SchannelContext::getPeerCertificateVerificationError() const {
- return m_verificationError ? boost::make_shared<CertificateVerificationError>(*m_verificationError) : CertificateVerificationError::ref();
+ return verificationError_ ? std::make_shared<CertificateVerificationError>(*verificationError_) : CertificateVerificationError::ref();
}
//------------------------------------------------------------------------
ByteArray SchannelContext::getFinishMessage() const {
- SecPkgContext_Bindings bindings;
- int ret = QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_UNIQUE_BINDINGS, &bindings);
- if (ret == SEC_E_OK) {
- return createByteArray(((unsigned char*) bindings.Bindings) + bindings.Bindings->dwApplicationDataOffset + 11 /* tls-unique:*/, bindings.Bindings->cbApplicationDataLength - 11);
- }
- return ByteArray();
+ SecPkgContext_Bindings bindings;
+ int ret = QueryContextAttributes(contextHandle_, SECPKG_ATTR_UNIQUE_BINDINGS, &bindings);
+ if (ret == SEC_E_OK) {
+ return createByteArray(((unsigned char*) bindings.Bindings) + bindings.Bindings->dwApplicationDataOffset + 11 /* tls-unique:*/, bindings.Bindings->cbApplicationDataLength - 11);
+ }
+ else {
+ SWIFT_LOG(debug) << "Error while retrieving Finish Message";
+ }
+
+ return ByteArray();
}
//------------------------------------------------------------------------
void SchannelContext::setCheckCertificateRevocation(bool b) {
- checkCertificateRevocation = b;
+ checkCertificateRevocation_ = b;
+}
+
+void SchannelContext::setDisconnectOnCardRemoval(bool b) {
+ disconnectOnCardRemoval_ = b;
}
diff --git a/Swiften/TLS/Schannel/SchannelContext.h b/Swiften/TLS/Schannel/SchannelContext.h
index 8603498..a3748fe 100644
--- a/Swiften/TLS/Schannel/SchannelContext.h
+++ b/Swiften/TLS/Schannel/SchannelContext.h
@@ -5,14 +5,14 @@
*/
/*
- * Copyright (c) 2012 Kevin Smith
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2012-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <Swiften/Base/boost_bsignals.h>
+#include <boost/signals2.hpp>
#include <Swiften/TLS/TLSContext.h>
#include <Swiften/TLS/Schannel/SchannelUtil.h>
@@ -28,81 +28,85 @@
#include <boost/noncopyable.hpp>
-namespace Swift
-{
- class CAPICertificate;
- class SchannelContext : public TLSContext, boost::noncopyable
- {
- public:
- typedef boost::shared_ptr<SchannelContext> sp_t;
+namespace Swift
+{
+ class CAPICertificate;
+ class SchannelContext : public TLSContext, boost::noncopyable
+ {
+ public:
+ typedef std::shared_ptr<SchannelContext> sp_t;
- public:
- SchannelContext();
+ public:
+ SchannelContext(bool tls1_0Workaround);
- ~SchannelContext();
+ virtual ~SchannelContext();
- //
- // TLSContext
- //
- virtual void connect();
- virtual bool setClientCertificate(CertificateWithKey::ref cert);
+ //
+ // TLSContext
+ //
+ virtual void connect();
+ virtual bool setClientCertificate(CertificateWithKey::ref cert);
- virtual void handleDataFromNetwork(const SafeByteArray& data);
- virtual void handleDataFromApplication(const SafeByteArray& data);
+ virtual void handleDataFromNetwork(const SafeByteArray& data);
+ virtual void handleDataFromApplication(const SafeByteArray& data);
- virtual std::vector<Certificate::ref> getPeerCertificateChain() const;
- virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const;
+ virtual std::vector<Certificate::ref> getPeerCertificateChain() const;
+ virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const;
- virtual ByteArray getFinishMessage() const;
+ virtual ByteArray getFinishMessage() const;
- virtual void setCheckCertificateRevocation(bool b);
+ virtual void setCheckCertificateRevocation(bool b);
- private:
- void determineStreamSizes();
- void continueHandshake(const SafeByteArray& data);
- void indicateError(boost::shared_ptr<TLSError> error);
- //FIXME: Remove
- void indicateError() {indicateError(boost::make_shared<TLSError>());}
- void handleCertError(SECURITY_STATUS status) ;
+ virtual void setDisconnectOnCardRemoval(bool b);
- void sendDataOnNetwork(const void* pData, size_t dataSize);
- void forwardDataToApplication(const void* pData, size_t dataSize);
+ private:
+ void determineStreamSizes();
+ void continueHandshake(const SafeByteArray& data);
+ void indicateError(std::shared_ptr<TLSError> error);
+ //FIXME: Remove
+ void indicateError() {indicateError(std::make_shared<TLSError>());}
+ void handleCertError(SECURITY_STATUS status) ;
- void decryptAndProcessData(const SafeByteArray& data);
- void encryptAndSendData(const SafeByteArray& data);
+ void sendDataOnNetwork(const void* pData, size_t dataSize);
+ void forwardDataToApplication(const void* pData, size_t dataSize);
- void appendNewData(const SafeByteArray& data);
- SECURITY_STATUS validateServerCertificate();
+ void decryptAndProcessData(const SafeByteArray& data);
+ void encryptAndSendData(const SafeByteArray& data);
- void handleCertificateCardRemoved();
+ void appendNewData(const SafeByteArray& data);
+ SECURITY_STATUS validateServerCertificate();
- private:
- enum SchannelState
- {
- Start,
- Connecting,
- Connected,
- Error
+ void handleCertificateCardRemoved();
- };
+ private:
+ enum SchannelState
+ {
+ Start,
+ Connecting,
+ Connected,
+ Error
- SchannelState m_state;
- boost::optional<CertificateVerificationError> m_verificationError;
+ };
- ULONG m_secContext;
- ScopedCredHandle m_credHandle;
- ScopedCtxtHandle m_ctxtHandle;
- DWORD m_ctxtFlags;
- SecPkgContext_StreamSizes m_streamSizes;
+ SchannelState state_;
+ boost::optional<CertificateVerificationError> verificationError_;
- std::vector<char> m_receivedData;
+ ULONG secContext_;
+ ScopedCredHandle credHandle_;
+ ScopedCtxtHandle contextHandle_;
+ DWORD contextFlags_;
+ SecPkgContext_StreamSizes streamSizes_;
- HCERTSTORE m_my_cert_store;
- std::string m_cert_store_name;
- std::string m_cert_name;
+ std::vector<char> receivedData_;
+
+ HCERTSTORE myCertStore_;
+ std::string certStoreName_;
+ std::string certName_;
////Not needed, most likely
- std::string m_smartcard_reader; //Can be empty string for non SmartCard certificates
- boost::shared_ptr<CAPICertificate> userCertificate;
- bool checkCertificateRevocation;
- };
+ std::string smartCardReader_; //Can be empty string for non SmartCard certificates
+ std::shared_ptr<CAPICertificate> userCertificate_;
+ bool checkCertificateRevocation_;
+ bool tls1_0Workaround_;
+ bool disconnectOnCardRemoval_;
+ };
}
diff --git a/Swiften/TLS/Schannel/SchannelContextFactory.cpp b/Swiften/TLS/Schannel/SchannelContextFactory.cpp
index 8b0044c..d029730 100644
--- a/Swiften/TLS/Schannel/SchannelContextFactory.cpp
+++ b/Swiften/TLS/Schannel/SchannelContextFactory.cpp
@@ -4,27 +4,40 @@
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
-#include "Swiften/TLS/Schannel/SchannelContextFactory.h"
-#include "Swiften/TLS/Schannel/SchannelContext.h"
+/*
+ * Copyright (c) 2015-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/TLS/Schannel/SchannelContextFactory.h>
+
+#include <Swiften/TLS/Schannel/SchannelContext.h>
namespace Swift {
-SchannelContextFactory::SchannelContextFactory() : checkCertificateRevocation(true) {
+SchannelContextFactory::SchannelContextFactory() : checkCertificateRevocation(true), disconnectOnCardRemoval(true) {
}
bool SchannelContextFactory::canCreate() const {
- return true;
+ return true;
}
-TLSContext* SchannelContextFactory::createTLSContext() {
- SchannelContext* context = new SchannelContext();
- context->setCheckCertificateRevocation(checkCertificateRevocation);
- return context;
+std::unique_ptr<TLSContext> SchannelContextFactory::createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode) {
+ // TLS server mode is not supported for the SecureTransport backend yet.
+ assert(mode == TLSContext::Mode::Client);
+ SchannelContext* context = new SchannelContext(tlsOptions.schannelTLS1_0Workaround);
+ context->setCheckCertificateRevocation(checkCertificateRevocation);
+ context->setDisconnectOnCardRemoval(disconnectOnCardRemoval);
+ return std::unique_ptr<TLSContext>(context);
}
void SchannelContextFactory::setCheckCertificateRevocation(bool b) {
- checkCertificateRevocation = b;
+ checkCertificateRevocation = b;
}
+void SchannelContextFactory::setDisconnectOnCardRemoval(bool b) {
+ disconnectOnCardRemoval = b;
+}
}
diff --git a/Swiften/TLS/Schannel/SchannelContextFactory.h b/Swiften/TLS/Schannel/SchannelContextFactory.h
index 9dc835c..76ff365 100644
--- a/Swiften/TLS/Schannel/SchannelContextFactory.h
+++ b/Swiften/TLS/Schannel/SchannelContextFactory.h
@@ -4,21 +4,31 @@
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
+/*
+ * Copyright (c) 2015-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
#pragma once
-#include "Swiften/TLS/TLSContextFactory.h"
+#include <memory>
+
+#include <Swiften/TLS/TLSContextFactory.h>
namespace Swift {
- class SchannelContextFactory : public TLSContextFactory {
- public:
- SchannelContextFactory();
+ class SchannelContextFactory : public TLSContextFactory {
+ public:
+ SchannelContextFactory();
- bool canCreate() const;
- virtual TLSContext* createTLSContext();
+ bool canCreate() const;
+ virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode = TLSContext::Mode::Client);
+ virtual void setCheckCertificateRevocation(bool b);
- virtual void setCheckCertificateRevocation(bool b);
+ virtual void setDisconnectOnCardRemoval(bool b);
- public:
- bool checkCertificateRevocation;
- };
+ public:
+ bool checkCertificateRevocation;
+ bool disconnectOnCardRemoval;
+ };
}
diff --git a/Swiften/TLS/Schannel/SchannelUtil.h b/Swiften/TLS/Schannel/SchannelUtil.h
index 4f73aac..ec71d9d 100644
--- a/Swiften/TLS/Schannel/SchannelUtil.h
+++ b/Swiften/TLS/Schannel/SchannelUtil.h
@@ -4,6 +4,12 @@
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
+/*
+ * Copyright (c) 2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
#pragma once
#define SECURITY_WIN32
@@ -14,412 +20,412 @@
#include <boost/noncopyable.hpp>
-namespace Swift
+namespace Swift
{
- //
- // Convenience wrapper around the Schannel CredHandle struct.
- //
- class ScopedCredHandle
- {
- private:
- struct HandleContext
- {
- HandleContext()
- {
- ZeroMemory(&m_h, sizeof(m_h));
- }
-
- HandleContext(const CredHandle& h)
- {
- memcpy(&m_h, &h, sizeof(m_h));
- }
-
- ~HandleContext()
- {
- ::FreeCredentialsHandle(&m_h);
- }
-
- CredHandle m_h;
- };
-
- public:
- ScopedCredHandle()
- : m_pHandle( new HandleContext )
- {
- }
-
- explicit ScopedCredHandle(const CredHandle& h)
- : m_pHandle( new HandleContext(h) )
- {
- }
-
- // Copy constructor
- explicit ScopedCredHandle(const ScopedCredHandle& rhs)
- {
- m_pHandle = rhs.m_pHandle;
- }
-
- ~ScopedCredHandle()
- {
- m_pHandle.reset();
- }
-
- PCredHandle Reset()
- {
- CloseHandle();
- return &m_pHandle->m_h;
- }
-
- operator PCredHandle() const
- {
- return &m_pHandle->m_h;
- }
-
- ScopedCredHandle& operator=(const ScopedCredHandle& sh)
- {
- // Only update the internal handle if it's different
- if (&m_pHandle->m_h != &sh.m_pHandle->m_h)
- {
- m_pHandle = sh.m_pHandle;
- }
-
- return *this;
- }
-
- void CloseHandle()
- {
- m_pHandle.reset( new HandleContext );
- }
-
- private:
- boost::shared_ptr<HandleContext> m_pHandle;
- };
-
- //------------------------------------------------------------------------
-
- //
- // Convenience wrapper around the Schannel CtxtHandle struct.
- //
- class ScopedCtxtHandle
- {
- private:
- struct HandleContext
- {
- HandleContext()
- {
- ZeroMemory(&m_h, sizeof(m_h));
- }
-
- ~HandleContext()
- {
- ::DeleteSecurityContext(&m_h);
- }
-
- CtxtHandle m_h;
- };
-
- public:
- ScopedCtxtHandle()
- : m_pHandle( new HandleContext )
- {
- }
-
- explicit ScopedCtxtHandle(CredHandle h)
- : m_pHandle( new HandleContext )
- {
- }
-
- // Copy constructor
- explicit ScopedCtxtHandle(const ScopedCtxtHandle& rhs)
- {
- m_pHandle = rhs.m_pHandle;
- }
-
- ~ScopedCtxtHandle()
- {
- m_pHandle.reset();
- }
-
- PCredHandle Reset()
- {
- CloseHandle();
- return &m_pHandle->m_h;
- }
-
- operator PCredHandle() const
- {
- return &m_pHandle->m_h;
- }
-
- ScopedCtxtHandle& operator=(const ScopedCtxtHandle& sh)
- {
- // Only update the internal handle if it's different
- if (&m_pHandle->m_h != &sh.m_pHandle->m_h)
- {
- m_pHandle = sh.m_pHandle;
- }
-
- return *this;
- }
-
- void CloseHandle()
- {
- m_pHandle.reset( new HandleContext );
- }
-
- private:
- boost::shared_ptr<HandleContext> m_pHandle;
- };
-
- //------------------------------------------------------------------------
-
- //
- // Convenience wrapper around the Schannel ScopedSecBuffer struct.
- //
- class ScopedSecBuffer : boost::noncopyable
- {
- public:
- ScopedSecBuffer(PSecBuffer pSecBuffer)
- : m_pSecBuffer(pSecBuffer)
- {
- }
-
- ~ScopedSecBuffer()
- {
- // Loop through all the output buffers and make sure we free them
- if (m_pSecBuffer->pvBuffer)
- FreeContextBuffer(m_pSecBuffer->pvBuffer);
- }
-
- PSecBuffer AsPtr()
- {
- return m_pSecBuffer;
- }
-
- PSecBuffer operator->()
- {
- return m_pSecBuffer;
- }
-
- private:
- PSecBuffer m_pSecBuffer;
- };
-
- //------------------------------------------------------------------------
-
- //
- // Convenience wrapper around the Schannel PCCERT_CONTEXT.
- //
- class ScopedCertContext
- {
- private:
- struct HandleContext
- {
- HandleContext()
- : m_pCertCtxt(NULL)
- {
- }
-
- HandleContext(PCCERT_CONTEXT pCert)
- : m_pCertCtxt(pCert)
- {
- }
-
- ~HandleContext()
- {
- if (m_pCertCtxt)
- CertFreeCertificateContext(m_pCertCtxt);
- }
-
- PCCERT_CONTEXT m_pCertCtxt;
- };
-
- public:
- ScopedCertContext()
- : m_pHandle( new HandleContext )
- {
- }
-
- explicit ScopedCertContext(PCCERT_CONTEXT pCert)
- : m_pHandle( new HandleContext(pCert) )
- {
- }
-
- // Copy constructor
- ScopedCertContext(const ScopedCertContext& rhs)
- {
- m_pHandle = rhs.m_pHandle;
- }
-
- ~ScopedCertContext()
- {
- m_pHandle.reset();
- }
-
- PCCERT_CONTEXT* Reset()
- {
- FreeContext();
- return &m_pHandle->m_pCertCtxt;
- }
-
- operator PCCERT_CONTEXT() const
- {
- return m_pHandle->m_pCertCtxt;
- }
-
- PCCERT_CONTEXT* GetPointer() const
- {
- return &m_pHandle->m_pCertCtxt;
- }
-
- PCCERT_CONTEXT operator->() const
- {
- return m_pHandle->m_pCertCtxt;
- }
-
- ScopedCertContext& operator=(const ScopedCertContext& sh)
- {
- // Only update the internal handle if it's different
- if (&m_pHandle->m_pCertCtxt != &sh.m_pHandle->m_pCertCtxt)
- {
- m_pHandle = sh.m_pHandle;
- }
-
- return *this;
- }
-
- ScopedCertContext& operator=(PCCERT_CONTEXT pCertCtxt)
- {
- // Only update the internal handle if it's different
- if (m_pHandle && m_pHandle->m_pCertCtxt != pCertCtxt)
- m_pHandle.reset( new HandleContext(pCertCtxt) );
-
- return *this;
- }
-
- void FreeContext()
- {
- m_pHandle.reset( new HandleContext );
- }
-
- private:
- boost::shared_ptr<HandleContext> m_pHandle;
- };
-
- //------------------------------------------------------------------------
-
- //
- // Convenience wrapper around the Schannel HCERTSTORE.
- //
- class ScopedCertStore : boost::noncopyable
- {
- public:
- ScopedCertStore(HCERTSTORE hCertStore)
- : m_hCertStore(hCertStore)
- {
- }
-
- ~ScopedCertStore()
- {
- // Forcefully free all memory related to the store, i.e. we assume all CertContext's that have been opened via this
- // cert store have been closed at this point.
- if (m_hCertStore)
- CertCloseStore(m_hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
- }
-
- operator HCERTSTORE() const
- {
- return m_hCertStore;
- }
-
- private:
- HCERTSTORE m_hCertStore;
- };
-
- //------------------------------------------------------------------------
-
- //
- // Convenience wrapper around the Schannel CERT_CHAIN_CONTEXT.
- //
- class ScopedCertChainContext
- {
- private:
- struct HandleContext
- {
- HandleContext()
- : m_pCertChainCtxt(NULL)
- {
- }
-
- HandleContext(PCCERT_CHAIN_CONTEXT pCert)
- : m_pCertChainCtxt(pCert)
- {
- }
-
- ~HandleContext()
- {
- if (m_pCertChainCtxt)
- CertFreeCertificateChain(m_pCertChainCtxt);
- }
-
- PCCERT_CHAIN_CONTEXT m_pCertChainCtxt;
- };
-
- public:
- ScopedCertChainContext()
- : m_pHandle( new HandleContext )
- {
- }
-
- explicit ScopedCertChainContext(PCCERT_CHAIN_CONTEXT pCert)
- : m_pHandle( new HandleContext(pCert) )
- {
- }
-
- // Copy constructor
- ScopedCertChainContext(const ScopedCertChainContext& rhs)
- {
- m_pHandle = rhs.m_pHandle;
- }
-
- ~ScopedCertChainContext()
- {
- m_pHandle.reset();
- }
-
- PCCERT_CHAIN_CONTEXT* Reset()
- {
- FreeContext();
- return &m_pHandle->m_pCertChainCtxt;
- }
-
- operator PCCERT_CHAIN_CONTEXT() const
- {
- return m_pHandle->m_pCertChainCtxt;
- }
-
- PCCERT_CHAIN_CONTEXT operator->() const
- {
- return m_pHandle->m_pCertChainCtxt;
- }
-
- ScopedCertChainContext& operator=(const ScopedCertChainContext& sh)
- {
- // Only update the internal handle if it's different
- if (&m_pHandle->m_pCertChainCtxt != &sh.m_pHandle->m_pCertChainCtxt)
- {
- m_pHandle = sh.m_pHandle;
- }
-
- return *this;
- }
-
- void FreeContext()
- {
- m_pHandle.reset( new HandleContext );
- }
-
- private:
- boost::shared_ptr<HandleContext> m_pHandle;
- };
+ //
+ // Convenience wrapper around the Schannel CredHandle struct.
+ //
+ class ScopedCredHandle
+ {
+ private:
+ struct HandleContext
+ {
+ HandleContext()
+ {
+ ZeroMemory(&m_h, sizeof(m_h));
+ }
+
+ HandleContext(const CredHandle& h)
+ {
+ memcpy(&m_h, &h, sizeof(m_h));
+ }
+
+ ~HandleContext()
+ {
+ ::FreeCredentialsHandle(&m_h);
+ }
+
+ CredHandle m_h;
+ };
+
+ public:
+ ScopedCredHandle()
+ : m_pHandle( new HandleContext )
+ {
+ }
+
+ explicit ScopedCredHandle(const CredHandle& h)
+ : m_pHandle( new HandleContext(h) )
+ {
+ }
+
+ // Copy constructor
+ explicit ScopedCredHandle(const ScopedCredHandle& rhs)
+ {
+ m_pHandle = rhs.m_pHandle;
+ }
+
+ ~ScopedCredHandle()
+ {
+ m_pHandle.reset();
+ }
+
+ PCredHandle Reset()
+ {
+ CloseHandle();
+ return &m_pHandle->m_h;
+ }
+
+ operator PCredHandle() const
+ {
+ return &m_pHandle->m_h;
+ }
+
+ ScopedCredHandle& operator=(const ScopedCredHandle& sh)
+ {
+ // Only update the internal handle if it's different
+ if (&m_pHandle->m_h != &sh.m_pHandle->m_h)
+ {
+ m_pHandle = sh.m_pHandle;
+ }
+
+ return *this;
+ }
+
+ void CloseHandle()
+ {
+ m_pHandle.reset( new HandleContext );
+ }
+
+ private:
+ std::shared_ptr<HandleContext> m_pHandle;
+ };
+
+ //------------------------------------------------------------------------
+
+ //
+ // Convenience wrapper around the Schannel CtxtHandle struct.
+ //
+ class ScopedCtxtHandle
+ {
+ private:
+ struct HandleContext
+ {
+ HandleContext()
+ {
+ ZeroMemory(&m_h, sizeof(m_h));
+ }
+
+ ~HandleContext()
+ {
+ ::DeleteSecurityContext(&m_h);
+ }
+
+ CtxtHandle m_h;
+ };
+
+ public:
+ ScopedCtxtHandle()
+ : m_pHandle( new HandleContext )
+ {
+ }
+
+ explicit ScopedCtxtHandle(CredHandle h)
+ : m_pHandle( new HandleContext )
+ {
+ }
+
+ // Copy constructor
+ explicit ScopedCtxtHandle(const ScopedCtxtHandle& rhs)
+ {
+ m_pHandle = rhs.m_pHandle;
+ }
+
+ ~ScopedCtxtHandle()
+ {
+ m_pHandle.reset();
+ }
+
+ PCredHandle Reset()
+ {
+ CloseHandle();
+ return &m_pHandle->m_h;
+ }
+
+ operator PCredHandle() const
+ {
+ return &m_pHandle->m_h;
+ }
+
+ ScopedCtxtHandle& operator=(const ScopedCtxtHandle& sh)
+ {
+ // Only update the internal handle if it's different
+ if (&m_pHandle->m_h != &sh.m_pHandle->m_h)
+ {
+ m_pHandle = sh.m_pHandle;
+ }
+
+ return *this;
+ }
+
+ void CloseHandle()
+ {
+ m_pHandle.reset( new HandleContext );
+ }
+
+ private:
+ std::shared_ptr<HandleContext> m_pHandle;
+ };
+
+ //------------------------------------------------------------------------
+
+ //
+ // Convenience wrapper around the Schannel ScopedSecBuffer struct.
+ //
+ class ScopedSecBuffer : boost::noncopyable
+ {
+ public:
+ ScopedSecBuffer(PSecBuffer pSecBuffer)
+ : m_pSecBuffer(pSecBuffer)
+ {
+ }
+
+ ~ScopedSecBuffer()
+ {
+ // Loop through all the output buffers and make sure we free them
+ if (m_pSecBuffer->pvBuffer)
+ FreeContextBuffer(m_pSecBuffer->pvBuffer);
+ }
+
+ PSecBuffer AsPtr()
+ {
+ return m_pSecBuffer;
+ }
+
+ PSecBuffer operator->()
+ {
+ return m_pSecBuffer;
+ }
+
+ private:
+ PSecBuffer m_pSecBuffer;
+ };
+
+ //------------------------------------------------------------------------
+
+ //
+ // Convenience wrapper around the Schannel PCCERT_CONTEXT.
+ //
+ class ScopedCertContext
+ {
+ private:
+ struct HandleContext
+ {
+ HandleContext()
+ : m_pCertCtxt(NULL)
+ {
+ }
+
+ HandleContext(PCCERT_CONTEXT pCert)
+ : m_pCertCtxt(pCert)
+ {
+ }
+
+ ~HandleContext()
+ {
+ if (m_pCertCtxt)
+ CertFreeCertificateContext(m_pCertCtxt);
+ }
+
+ PCCERT_CONTEXT m_pCertCtxt;
+ };
+
+ public:
+ ScopedCertContext()
+ : m_pHandle( new HandleContext )
+ {
+ }
+
+ explicit ScopedCertContext(PCCERT_CONTEXT pCert)
+ : m_pHandle( new HandleContext(pCert) )
+ {
+ }
+
+ // Copy constructor
+ ScopedCertContext(const ScopedCertContext& rhs)
+ {
+ m_pHandle = rhs.m_pHandle;
+ }
+
+ ~ScopedCertContext()
+ {
+ m_pHandle.reset();
+ }
+
+ PCCERT_CONTEXT* Reset()
+ {
+ FreeContext();
+ return &m_pHandle->m_pCertCtxt;
+ }
+
+ operator PCCERT_CONTEXT() const
+ {
+ return m_pHandle->m_pCertCtxt;
+ }
+
+ PCCERT_CONTEXT* GetPointer() const
+ {
+ return &m_pHandle->m_pCertCtxt;
+ }
+
+ PCCERT_CONTEXT operator->() const
+ {
+ return m_pHandle->m_pCertCtxt;
+ }
+
+ ScopedCertContext& operator=(const ScopedCertContext& sh)
+ {
+ // Only update the internal handle if it's different
+ if (&m_pHandle->m_pCertCtxt != &sh.m_pHandle->m_pCertCtxt)
+ {
+ m_pHandle = sh.m_pHandle;
+ }
+
+ return *this;
+ }
+
+ ScopedCertContext& operator=(PCCERT_CONTEXT pCertCtxt)
+ {
+ // Only update the internal handle if it's different
+ if (m_pHandle && m_pHandle->m_pCertCtxt != pCertCtxt)
+ m_pHandle.reset( new HandleContext(pCertCtxt) );
+
+ return *this;
+ }
+
+ void FreeContext()
+ {
+ m_pHandle.reset( new HandleContext );
+ }
+
+ private:
+ std::shared_ptr<HandleContext> m_pHandle;
+ };
+
+ //------------------------------------------------------------------------
+
+ //
+ // Convenience wrapper around the Schannel HCERTSTORE.
+ //
+ class ScopedCertStore : boost::noncopyable
+ {
+ public:
+ ScopedCertStore(HCERTSTORE hCertStore)
+ : m_hCertStore(hCertStore)
+ {
+ }
+
+ ~ScopedCertStore()
+ {
+ // Forcefully free all memory related to the store, i.e. we assume all CertContext's that have been opened via this
+ // cert store have been closed at this point.
+ if (m_hCertStore)
+ CertCloseStore(m_hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
+ }
+
+ operator HCERTSTORE() const
+ {
+ return m_hCertStore;
+ }
+
+ private:
+ HCERTSTORE m_hCertStore;
+ };
+
+ //------------------------------------------------------------------------
+
+ //
+ // Convenience wrapper around the Schannel CERT_CHAIN_CONTEXT.
+ //
+ class ScopedCertChainContext
+ {
+ private:
+ struct HandleContext
+ {
+ HandleContext()
+ : m_pCertChainCtxt(NULL)
+ {
+ }
+
+ HandleContext(PCCERT_CHAIN_CONTEXT pCert)
+ : m_pCertChainCtxt(pCert)
+ {
+ }
+
+ ~HandleContext()
+ {
+ if (m_pCertChainCtxt)
+ CertFreeCertificateChain(m_pCertChainCtxt);
+ }
+
+ PCCERT_CHAIN_CONTEXT m_pCertChainCtxt;
+ };
+
+ public:
+ ScopedCertChainContext()
+ : m_pHandle( new HandleContext )
+ {
+ }
+
+ explicit ScopedCertChainContext(PCCERT_CHAIN_CONTEXT pCert)
+ : m_pHandle( new HandleContext(pCert) )
+ {
+ }
+
+ // Copy constructor
+ ScopedCertChainContext(const ScopedCertChainContext& rhs)
+ {
+ m_pHandle = rhs.m_pHandle;
+ }
+
+ ~ScopedCertChainContext()
+ {
+ m_pHandle.reset();
+ }
+
+ PCCERT_CHAIN_CONTEXT* Reset()
+ {
+ FreeContext();
+ return &m_pHandle->m_pCertChainCtxt;
+ }
+
+ operator PCCERT_CHAIN_CONTEXT() const
+ {
+ return m_pHandle->m_pCertChainCtxt;
+ }
+
+ PCCERT_CHAIN_CONTEXT operator->() const
+ {
+ return m_pHandle->m_pCertChainCtxt;
+ }
+
+ ScopedCertChainContext& operator=(const ScopedCertChainContext& sh)
+ {
+ // Only update the internal handle if it's different
+ if (&m_pHandle->m_pCertChainCtxt != &sh.m_pHandle->m_pCertChainCtxt)
+ {
+ m_pHandle = sh.m_pHandle;
+ }
+
+ return *this;
+ }
+
+ void FreeContext()
+ {
+ m_pHandle.reset( new HandleContext );
+ }
+
+ private:
+ std::shared_ptr<HandleContext> m_pHandle;
+ };
}
diff --git a/Swiften/TLS/SecureTransport/SecureTransportCertificate.h b/Swiften/TLS/SecureTransport/SecureTransportCertificate.h
new file mode 100644
index 0000000..7faf3be
--- /dev/null
+++ b/Swiften/TLS/SecureTransport/SecureTransportCertificate.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <boost/type_traits.hpp>
+
+#include <Security/SecCertificate.h>
+
+#include <Swiften/TLS/Certificate.h>
+
+namespace Swift {
+
+class SecureTransportCertificate : public Certificate {
+public:
+ SecureTransportCertificate(SecCertificateRef certificate);
+ SecureTransportCertificate(const ByteArray& der);
+ virtual ~SecureTransportCertificate();
+
+ virtual std::string getSubjectName() const;
+ virtual std::vector<std::string> getCommonNames() const;
+ virtual std::vector<std::string> getSRVNames() const;
+ virtual std::vector<std::string> getDNSNames() const;
+ virtual std::vector<std::string> getXMPPAddresses() const;
+
+ virtual ByteArray toDER() const;
+
+private:
+ void parse();
+ typedef boost::remove_pointer<SecCertificateRef>::type SecCertificate;
+
+private:
+ std::shared_ptr<SecCertificate> certificateHandle_;
+ std::string subjectName_;
+ std::vector<std::string> commonNames_;
+ std::vector<std::string> srvNames_;
+ std::vector<std::string> dnsNames_;
+ std::vector<std::string> xmppAddresses_;
+};
+
+}
diff --git a/Swiften/TLS/SecureTransport/SecureTransportCertificate.mm b/Swiften/TLS/SecureTransport/SecureTransportCertificate.mm
new file mode 100644
index 0000000..fffb3ed
--- /dev/null
+++ b/Swiften/TLS/SecureTransport/SecureTransportCertificate.mm
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2015-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/TLS/SecureTransport/SecureTransportCertificate.h>
+
+#include <boost/numeric/conversion/cast.hpp>
+
+#include <Cocoa/Cocoa.h>
+#include <Security/Security.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace {
+
+template <typename T, typename S>
+T bridge_cast(S source) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+ return (__bridge T)(source);
+#pragma clang diagnostic pop
+}
+
+}
+
+namespace {
+
+inline std::string ns2StdString(NSString* _Nullable nsString);
+inline std::string ns2StdString(NSString* _Nullable nsString) {
+ std::string stdString;
+ if (nsString != nil) {
+ stdString = std::string([nsString cStringUsingEncoding:NSUTF8StringEncoding]);
+ }
+ return stdString;
+}
+
+}
+
+namespace Swift {
+
+SecureTransportCertificate::SecureTransportCertificate(SecCertificateRef certificate) {
+ assert(certificate);
+ CFRetain(certificate);
+ certificateHandle_ = std::shared_ptr<SecCertificate>(certificate, CFRelease);
+ parse();
+}
+
+
+SecureTransportCertificate::SecureTransportCertificate(const ByteArray& der) {
+ CFDataRef derData = CFDataCreateWithBytesNoCopy(nullptr, der.data(), static_cast<CFIndex>(der.size()), nullptr);
+ // certificate will take ownership of derData and free it on its release.
+ SecCertificateRef certificate = SecCertificateCreateWithData(nullptr, derData);
+ if (certificate) {
+ certificateHandle_ = std::shared_ptr<SecCertificate>(certificate, CFRelease);
+ parse();
+ }
+}
+
+SecureTransportCertificate::~SecureTransportCertificate() {
+
+}
+
+void SecureTransportCertificate::parse() {
+ assert(certificateHandle_);
+ CFErrorRef error = nullptr;
+
+ // The SecCertificateCopyValues function is not part of the iOS Secure Transport API.
+ CFDictionaryRef valueDict = SecCertificateCopyValues(certificateHandle_.get(), nullptr, &error);
+ if (valueDict) {
+ // Handle subject.
+ CFStringRef subject = SecCertificateCopySubjectSummary(certificateHandle_.get());
+ if (subject) {
+ NSString* subjectStr = bridge_cast<NSString*>(subject);
+ subjectName_ = ns2StdString(subjectStr);
+ CFRelease(subject);
+ }
+
+ // Handle a single Common Name.
+ CFStringRef commonName = nullptr;
+ OSStatus error = SecCertificateCopyCommonName(certificateHandle_.get(), &commonName);
+ if (!error && commonName) {
+ NSString* commonNameStr = bridge_cast<NSString*>(commonName);
+ commonNames_.push_back(ns2StdString(commonNameStr));
+ }
+ if (commonName) {
+ CFRelease(commonName);
+ }
+
+ // Handle Subject Alternative Names
+ NSDictionary* certDict = bridge_cast<NSDictionary*>(valueDict);
+ NSDictionary* subjectAltNamesDict = certDict[@"2.5.29.17"][@"value"];
+
+ for (NSDictionary* entry in subjectAltNamesDict) {
+ NSString* label = entry[@"label"];
+ if ([label isEqualToString:static_cast<NSString * _Nonnull>([NSString stringWithUTF8String:ID_ON_XMPPADDR_OID])]) {
+ xmppAddresses_.push_back(ns2StdString(entry[@"value"]));
+ }
+ else if ([label isEqualToString:static_cast<NSString * _Nonnull>([NSString stringWithUTF8String:ID_ON_DNSSRV_OID])]) {
+ srvNames_.push_back(ns2StdString(entry[@"value"]));
+ }
+ else if ([label isEqualToString:@"DNS Name"]) {
+ dnsNames_.push_back(ns2StdString(entry[@"value"]));
+ }
+ }
+ CFRelease(valueDict);
+ }
+
+ if (error) {
+ CFRelease(error);
+ }
+}
+
+std::string SecureTransportCertificate::getSubjectName() const {
+ return subjectName_;
+}
+
+std::vector<std::string> SecureTransportCertificate::getCommonNames() const {
+ return commonNames_;
+}
+
+std::vector<std::string> SecureTransportCertificate::getSRVNames() const {
+ return srvNames_;
+}
+
+std::vector<std::string> SecureTransportCertificate::getDNSNames() const {
+ return dnsNames_;
+}
+
+std::vector<std::string> SecureTransportCertificate::getXMPPAddresses() const {
+ return xmppAddresses_;
+}
+
+ByteArray SecureTransportCertificate::toDER() const {
+ ByteArray der;
+ if (certificateHandle_) {
+ CFDataRef derData = SecCertificateCopyData(certificateHandle_.get());
+ if (derData) {
+ try {
+ size_t dataSize = boost::numeric_cast<size_t>(CFDataGetLength(derData));
+ der.resize(dataSize);
+ CFDataGetBytes(derData, CFRangeMake(0,CFDataGetLength(derData)), der.data());
+ } catch (...) {
+ }
+ CFRelease(derData);
+ }
+ }
+ return der;
+}
+
+}
diff --git a/Swiften/TLS/SecureTransport/SecureTransportCertificateFactory.h b/Swiften/TLS/SecureTransport/SecureTransportCertificateFactory.h
new file mode 100644
index 0000000..3ea469d
--- /dev/null
+++ b/Swiften/TLS/SecureTransport/SecureTransportCertificateFactory.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Swiften/TLS/CertificateFactory.h>
+#include <Swiften/TLS/SecureTransport/SecureTransportCertificate.h>
+
+namespace Swift {
+
+class SecureTransportCertificateFactory : public CertificateFactory {
+ public:
+ virtual Certificate* createCertificateFromDER(const ByteArray& der) {
+ return new SecureTransportCertificate(der);
+ }
+ };
+}
diff --git a/Swiften/TLS/SecureTransport/SecureTransportContext.h b/Swiften/TLS/SecureTransport/SecureTransportContext.h
new file mode 100644
index 0000000..3942904
--- /dev/null
+++ b/Swiften/TLS/SecureTransport/SecureTransportContext.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <Security/SecureTransport.h>
+
+#include <Swiften/TLS/TLSContext.h>
+
+namespace Swift {
+
+class SecureTransportContext : public TLSContext {
+ public:
+ SecureTransportContext(bool checkCertificateRevocation);
+ virtual ~SecureTransportContext();
+
+ virtual void connect();
+
+ virtual bool setClientCertificate(CertificateWithKey::ref cert);
+
+ virtual void handleDataFromNetwork(const SafeByteArray&);
+ virtual void handleDataFromApplication(const SafeByteArray&);
+
+ virtual std::vector<Certificate::ref> getPeerCertificateChain() const;
+ virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const;
+
+ virtual ByteArray getFinishMessage() const;
+
+ private:
+ static OSStatus SSLSocketReadCallback(SSLConnectionRef connection, void *data, size_t *dataLength);
+ static OSStatus SSLSocketWriteCallback(SSLConnectionRef connection, const void *data, size_t *dataLength);
+
+ private:
+ enum State { None, Handshake, HandshakeDone, Error};
+ static std::string stateToString(State state);
+ void setState(State newState);
+
+ static std::shared_ptr<TLSError> nativeToTLSError(OSStatus error);
+ std::shared_ptr<CertificateVerificationError> CSSMErrorToVerificationError(OSStatus resultCode);
+
+ void processHandshake();
+ void verifyServerCertificate();
+
+ void fatalError(std::shared_ptr<TLSError> error, std::shared_ptr<CertificateVerificationError> certificateError);
+
+ private:
+ std::shared_ptr<SSLContext> sslContext_;
+ SafeByteArray readingBuffer_;
+ State state_;
+ CertificateVerificationError::ref verificationError_;
+ CertificateWithKey::ref clientCertificate_;
+ bool checkCertificateRevocation_;
+};
+
+}
diff --git a/Swiften/TLS/SecureTransport/SecureTransportContext.mm b/Swiften/TLS/SecureTransport/SecureTransportContext.mm
new file mode 100644
index 0000000..b4f7842
--- /dev/null
+++ b/Swiften/TLS/SecureTransport/SecureTransportContext.mm
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2015-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/TLS/SecureTransport/SecureTransportContext.h>
+
+#include <boost/type_traits.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/TLS/SecureTransport/SecureTransportCertificate.h>
+#include <Swiften/TLS/PKCS12Certificate.h>
+#include <Swiften/TLS/CertificateWithKey.h>
+
+#include <Cocoa/Cocoa.h>
+
+#import <Security/SecCertificate.h>
+#import <Security/SecImportExport.h>
+
+namespace {
+ typedef boost::remove_pointer<CFArrayRef>::type CFArray;
+ typedef boost::remove_pointer<SecTrustRef>::type SecTrust;
+}
+
+template <typename T, typename S>
+T bridge_cast(S source) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+ return (__bridge T)(source);
+#pragma clang diagnostic pop
+}
+
+namespace Swift {
+
+namespace {
+
+
+CFArrayRef CreateClientCertificateChainAsCFArrayRef(CertificateWithKey::ref key) {
+ std::shared_ptr<PKCS12Certificate> pkcs12 = std::dynamic_pointer_cast<PKCS12Certificate>(key);
+ if (!key) {
+ return nullptr;
+ }
+
+ SafeByteArray safePassword = pkcs12->getPassword();
+ CFIndex passwordSize = 0;
+ try {
+ passwordSize = boost::numeric_cast<CFIndex>(safePassword.size());
+ } catch (...) {
+ return nullptr;
+ }
+
+ CFMutableArrayRef certChain = CFArrayCreateMutable(nullptr, 0, nullptr);
+
+ OSStatus securityError = errSecSuccess;
+ CFStringRef password = CFStringCreateWithBytes(kCFAllocatorDefault, safePassword.data(), passwordSize, kCFStringEncodingUTF8, false);
+ const void* keys[] = { kSecImportExportPassphrase };
+ const void* values[] = { password };
+
+ CFDictionaryRef options = CFDictionaryCreate(nullptr, keys, values, 1, nullptr, nullptr);
+
+ CFArrayRef items = nullptr;
+ CFDataRef pkcs12Data = bridge_cast<CFDataRef>([NSData dataWithBytes: static_cast<const void *>(pkcs12->getData().data()) length:pkcs12->getData().size()]);
+ securityError = SecPKCS12Import(pkcs12Data, options, &items);
+ CFRelease(options);
+ NSArray* nsItems = bridge_cast<NSArray*>(items);
+
+ switch(securityError) {
+ case errSecSuccess:
+ break;
+ case errSecAuthFailed:
+ // Password did not work for decoding the certificate.
+ SWIFT_LOG(warning) << "Invalid password.";
+ break;
+ case errSecDecode:
+ // Other decoding error.
+ SWIFT_LOG(warning) << "PKCS12 decoding error.";
+ break;
+ default:
+ SWIFT_LOG(warning) << "Unknown error.";
+ }
+
+ if (securityError != errSecSuccess) {
+ if (items) {
+ CFRelease(items);
+ items = nullptr;
+ }
+ CFRelease(certChain);
+ certChain = nullptr;
+ }
+
+ if (certChain) {
+ CFArrayAppendValue(certChain, nsItems[0][@"identity"]);
+
+ for (CFIndex index = 0; index < CFArrayGetCount(bridge_cast<CFArrayRef>(nsItems[0][@"chain"])); index++) {
+ CFArrayAppendValue(certChain, CFArrayGetValueAtIndex(bridge_cast<CFArrayRef>(nsItems[0][@"chain"]), index));
+ }
+ }
+ return certChain;
+}
+
+}
+
+SecureTransportContext::SecureTransportContext(bool checkCertificateRevocation) : state_(None), checkCertificateRevocation_(checkCertificateRevocation) {
+ sslContext_ = std::shared_ptr<SSLContext>(SSLCreateContext(nullptr, kSSLClientSide, kSSLStreamType), CFRelease);
+
+ OSStatus error = noErr;
+ // set IO callbacks
+ error = SSLSetIOFuncs(sslContext_.get(), &SecureTransportContext::SSLSocketReadCallback, &SecureTransportContext::SSLSocketWriteCallback);
+ if (error != noErr) {
+ SWIFT_LOG(error) << "Unable to set IO functions to SSL context.";
+ sslContext_.reset();
+ }
+
+ error = SSLSetConnection(sslContext_.get(), this);
+ if (error != noErr) {
+ SWIFT_LOG(error) << "Unable to set connection to SSL context.";
+ sslContext_.reset();
+ }
+
+
+ error = SSLSetSessionOption(sslContext_.get(), kSSLSessionOptionBreakOnServerAuth, true);
+ if (error != noErr) {
+ SWIFT_LOG(error) << "Unable to set kSSLSessionOptionBreakOnServerAuth on session.";
+ sslContext_.reset();
+ }
+}
+
+SecureTransportContext::~SecureTransportContext() {
+ if (sslContext_) {
+ SSLClose(sslContext_.get());
+ }
+}
+
+std::string SecureTransportContext::stateToString(State state) {
+ std::string returnValue;
+ switch(state) {
+ case Handshake:
+ returnValue = "Handshake";
+ break;
+ case HandshakeDone:
+ returnValue = "HandshakeDone";
+ break;
+ case None:
+ returnValue = "None";
+ break;
+ case Error:
+ returnValue = "Error";
+ break;
+ }
+ return returnValue;
+}
+
+void SecureTransportContext::setState(State newState) {
+ SWIFT_LOG(debug) << "Switch state from " << stateToString(state_) << " to " << stateToString(newState) << ".";
+ state_ = newState;
+}
+
+void SecureTransportContext::connect() {
+ SWIFT_LOG_ASSERT(state_ == None, error) << "current state '" << stateToString(state_) << " invalid.";
+ if (clientCertificate_) {
+ CFArrayRef certs = CreateClientCertificateChainAsCFArrayRef(clientCertificate_);
+ if (certs) {
+ std::shared_ptr<CFArray> certRefs(certs, CFRelease);
+ OSStatus result = SSLSetCertificate(sslContext_.get(), certRefs.get());
+ if (result != noErr) {
+ SWIFT_LOG(error) << "SSLSetCertificate failed with error " << result << ".";
+ }
+ }
+ }
+ processHandshake();
+}
+
+void SecureTransportContext::processHandshake() {
+ SWIFT_LOG_ASSERT(state_ == None || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid.";
+ OSStatus error = SSLHandshake(sslContext_.get());
+ if (error == errSSLWouldBlock) {
+ setState(Handshake);
+ }
+ else if (error == noErr) {
+ SWIFT_LOG(debug) << "TLS handshake successful.";
+ setState(HandshakeDone);
+ onConnected();
+ }
+ else if (error == errSSLPeerAuthCompleted) {
+ SWIFT_LOG(debug) << "Received server certificate. Start verification.";
+ setState(Handshake);
+ verifyServerCertificate();
+ }
+ else {
+ SWIFT_LOG(debug) << "Error returned from SSLHandshake call is " << error << ".";
+ fatalError(nativeToTLSError(error), std::make_shared<CertificateVerificationError>());
+ }
+}
+
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+void SecureTransportContext::verifyServerCertificate() {
+ SecTrustRef trust = nullptr;
+ OSStatus error = SSLCopyPeerTrust(sslContext_.get(), &trust);
+ if (error != noErr) {
+ fatalError(std::make_shared<TLSError>(), std::make_shared<CertificateVerificationError>());
+ return;
+ }
+ std::shared_ptr<SecTrust> trustRef = std::shared_ptr<SecTrust>(trust, CFRelease);
+
+ if (checkCertificateRevocation_) {
+ error = SecTrustSetOptions(trust, kSecTrustOptionRequireRevPerCert | kSecTrustOptionFetchIssuerFromNet);
+ if (error != noErr) {
+ fatalError(std::make_shared<TLSError>(), std::make_shared<CertificateVerificationError>());
+ return;
+ }
+ }
+
+ SecTrustResultType trustResult;
+ error = SecTrustEvaluate(trust, &trustResult);
+ if (error != errSecSuccess) {
+ fatalError(std::make_shared<TLSError>(), std::make_shared<CertificateVerificationError>());
+ return;
+ }
+
+ OSStatus cssmResult = 0;
+ switch(trustResult) {
+ case kSecTrustResultUnspecified:
+ SWIFT_LOG(warning) << "Successful implicit validation. Result unspecified.";
+ break;
+ case kSecTrustResultProceed:
+ SWIFT_LOG(warning) << "Validation resulted in explicitly trusted.";
+ break;
+ case kSecTrustResultRecoverableTrustFailure:
+ SWIFT_LOG(warning) << "recoverable trust failure";
+ error = SecTrustGetCssmResultCode(trust, &cssmResult);
+ if (error == errSecSuccess) {
+ verificationError_ = CSSMErrorToVerificationError(cssmResult);
+ if (cssmResult == CSSMERR_TP_VERIFY_ACTION_FAILED || cssmResult == CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK ) {
+ // Find out the reason why the verification failed.
+ CFArrayRef certChain;
+ CSSM_TP_APPLE_EVIDENCE_INFO* statusChain;
+ error = SecTrustGetResult(trustRef.get(), &trustResult, &certChain, &statusChain);
+ if (error == errSecSuccess) {
+ std::shared_ptr<CFArray> certChainRef = std::shared_ptr<CFArray>(certChain, CFRelease);
+ for (CFIndex index = 0; index < CFArrayGetCount(certChainRef.get()); index++) {
+ for (CFIndex n = 0; n < statusChain[index].NumStatusCodes; n++) {
+ // Even though Secure Transport reported CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK on the whole certificate
+ // chain, the actual cause can be that a revocation check for a specific cert returned CSSMERR_TP_CERT_REVOKED.
+ if (!verificationError_ || verificationError_->getType() == CertificateVerificationError::RevocationCheckFailed) {
+ verificationError_ = CSSMErrorToVerificationError(statusChain[index].StatusCodes[n]);
+ }
+ }
+ }
+ }
+ else {
+
+ }
+ }
+ }
+ else {
+ verificationError_ = std::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError);
+ }
+ break;
+ case kSecTrustResultInvalid:
+ verificationError_ = std::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError);
+ break;
+ case kSecTrustResultConfirm:
+ // TODO: Confirmation from the user is required before proceeding.
+ verificationError_ = std::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError);
+ break;
+ case kSecTrustResultDeny:
+ // The user specified that the certificate should not be trusted.
+ verificationError_ = std::make_shared<CertificateVerificationError>(CertificateVerificationError::Untrusted);
+ break;
+ case kSecTrustResultFatalTrustFailure:
+ // Trust denied; no simple fix is available.
+ verificationError_ = std::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError);
+ break;
+ case kSecTrustResultOtherError:
+ verificationError_ = std::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError);
+ break;
+ }
+
+ // We proceed with the TLS handshake here to give the application an opportunity
+ // to apply custom validation and trust management. The application is responsible
+ // to call \ref getPeerCertificateVerificationError directly after the \ref onConnected
+ // signal is called and before any application data is send to the context.
+ processHandshake();
+}
+
+#pragma clang diagnostic pop
+
+bool SecureTransportContext::setClientCertificate(CertificateWithKey::ref cert) {
+ CFArrayRef nativeClientChain = CreateClientCertificateChainAsCFArrayRef(cert);
+ if (nativeClientChain) {
+ clientCertificate_ = cert;
+ CFRelease(nativeClientChain);
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+void SecureTransportContext::handleDataFromNetwork(const SafeByteArray& data) {
+ SWIFT_LOG(debug);
+ SWIFT_LOG_ASSERT(state_ == HandshakeDone || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid.";
+
+ append(readingBuffer_, data);
+
+ size_t bytesRead = 0;
+ OSStatus error = noErr;
+ SafeByteArray applicationData;
+
+ switch(state_) {
+ case None:
+ assert(false && "Invalid state 'None'.");
+ break;
+ case Handshake:
+ processHandshake();
+ break;
+ case HandshakeDone:
+ while (error == noErr) {
+ applicationData.resize(readingBuffer_.size());
+ error = SSLRead(sslContext_.get(), applicationData.data(), applicationData.size(), &bytesRead);
+ if (error == noErr) {
+ // Read successful.
+ }
+ else if (error == errSSLWouldBlock) {
+ // Secure Transport does not want more data.
+ break;
+ }
+ else {
+ SWIFT_LOG(error) << "SSLRead failed with error " << error << ", read bytes: " << bytesRead << ".";
+ fatalError(std::make_shared<TLSError>(), std::make_shared<CertificateVerificationError>());
+ return;
+ }
+
+ if (bytesRead > 0) {
+ applicationData.resize(bytesRead);
+ onDataForApplication(applicationData);
+ }
+ else {
+ break;
+ }
+ }
+ break;
+ case Error:
+ SWIFT_LOG(debug) << "Igoring received data in error state.";
+ break;
+ }
+}
+
+
+void SecureTransportContext::handleDataFromApplication(const SafeByteArray& data) {
+ size_t processedBytes = 0;
+ OSStatus error = SSLWrite(sslContext_.get(), data.data(), data.size(), &processedBytes);
+ switch(error) {
+ case errSSLWouldBlock:
+ SWIFT_LOG(warning) << "Unexpected because the write callback does not block.";
+ return;
+ case errSSLClosedGraceful:
+ case noErr:
+ return;
+ default:
+ SWIFT_LOG(warning) << "SSLWrite returned error code: " << error << ", processed bytes: " << processedBytes;
+ fatalError(std::make_shared<TLSError>(), std::shared_ptr<CertificateVerificationError>());
+ }
+}
+
+std::vector<Certificate::ref> SecureTransportContext::getPeerCertificateChain() const {
+ std::vector<Certificate::ref> peerCertificateChain;
+
+ if (sslContext_) {
+ typedef boost::remove_pointer<SecTrustRef>::type SecTrust;
+ std::shared_ptr<SecTrust> securityTrust;
+
+ SecTrustRef secTrust = nullptr;
+ OSStatus error = SSLCopyPeerTrust(sslContext_.get(), &secTrust);
+ if (error == noErr) {
+ securityTrust = std::shared_ptr<SecTrust>(secTrust, CFRelease);
+
+ CFIndex chainSize = SecTrustGetCertificateCount(securityTrust.get());
+ for (CFIndex n = 0; n < chainSize; n++) {
+ SecCertificateRef certificate = SecTrustGetCertificateAtIndex(securityTrust.get(), n);
+ if (certificate) {
+ peerCertificateChain.push_back(std::make_shared<SecureTransportCertificate>(certificate));
+ }
+ }
+ }
+ else {
+ SWIFT_LOG(warning) << "Failed to obtain peer trust structure; error = " << error << ".";
+ }
+ }
+
+ return peerCertificateChain;
+}
+
+CertificateVerificationError::ref SecureTransportContext::getPeerCertificateVerificationError() const {
+ return verificationError_;
+}
+
+ByteArray SecureTransportContext::getFinishMessage() const {
+ SWIFT_LOG(warning) << "Access to TLS handshake finish message is not part of OS X Secure Transport APIs.";
+ return ByteArray();
+}
+
+/**
+ * This I/O callback simulates an asynchronous read to the read buffer of the context. If it is empty, it returns errSSLWouldBlock; else
+ * the data within the buffer is returned.
+ */
+OSStatus SecureTransportContext::SSLSocketReadCallback(SSLConnectionRef connection, void *data, size_t *dataLength) {
+ SecureTransportContext* context = const_cast<SecureTransportContext*>(static_cast<const SecureTransportContext*>(connection));
+ OSStatus retValue = noErr;
+
+ if (context->readingBuffer_.size() < *dataLength) {
+ // Would block because Secure Transport is trying to read more data than there currently is available in the buffer.
+ *dataLength = 0;
+ retValue = errSSLWouldBlock;
+ }
+ else {
+ size_t bufferLen = *dataLength;
+ size_t copyToBuffer = bufferLen < context->readingBuffer_.size() ? bufferLen : context->readingBuffer_.size();
+
+ memcpy(data, context->readingBuffer_.data(), copyToBuffer);
+
+ context->readingBuffer_ = SafeByteArray(context->readingBuffer_.data() + copyToBuffer, context->readingBuffer_.data() + context->readingBuffer_.size());
+ *dataLength = copyToBuffer;
+ }
+ return retValue;
+}
+
+OSStatus SecureTransportContext::SSLSocketWriteCallback(SSLConnectionRef connection, const void *data, size_t *dataLength) {
+ SecureTransportContext* context = const_cast<SecureTransportContext*>(static_cast<const SecureTransportContext*>(connection));
+ OSStatus retValue = noErr;
+
+ SafeByteArray safeData;
+ safeData.resize(*dataLength);
+ memcpy(safeData.data(), data, safeData.size());
+
+ context->onDataForNetwork(safeData);
+ return retValue;
+}
+
+std::shared_ptr<TLSError> SecureTransportContext::nativeToTLSError(OSStatus /* error */) {
+ std::shared_ptr<TLSError> swiftenError;
+ swiftenError = std::make_shared<TLSError>();
+ return swiftenError;
+}
+
+std::shared_ptr<CertificateVerificationError> SecureTransportContext::CSSMErrorToVerificationError(OSStatus resultCode) {
+ std::shared_ptr<CertificateVerificationError> error;
+ switch(resultCode) {
+ case CSSMERR_TP_NOT_TRUSTED:
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_NOT_TRUSTED";
+ error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::Untrusted);
+ break;
+ case CSSMERR_TP_CERT_NOT_VALID_YET:
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_NOT_VALID_YET";
+ error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::NotYetValid);
+ break;
+ case CSSMERR_TP_CERT_EXPIRED:
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_EXPIRED";
+ error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::Expired);
+ break;
+ case CSSMERR_TP_CERT_REVOKED:
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_REVOKED";
+ error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::Revoked);
+ break;
+ case CSSMERR_TP_VERIFY_ACTION_FAILED:
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_VERIFY_ACTION_FAILED";
+ break;
+ case CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK:
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK";
+ if (checkCertificateRevocation_) {
+ error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::RevocationCheckFailed);
+ }
+ break;
+ case CSSMERR_APPLETP_OCSP_UNAVAILABLE:
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_OCSP_UNAVAILABLE";
+ if (checkCertificateRevocation_) {
+ error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::RevocationCheckFailed);
+ }
+ break;
+ case CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE:
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE";
+ error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidPurpose);
+ break;
+ default:
+ SWIFT_LOG(warning) << "unhandled CSSM error: " << resultCode << ", CSSM_TP_BASE_TP_ERROR: " << CSSM_TP_BASE_TP_ERROR;
+ error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError);
+ break;
+ }
+ return error;
+}
+
+void SecureTransportContext::fatalError(std::shared_ptr<TLSError> error, std::shared_ptr<CertificateVerificationError> certificateError) {
+ setState(Error);
+ if (sslContext_) {
+ SSLClose(sslContext_.get());
+ }
+ verificationError_ = certificateError;
+ onError(error);
+}
+
+}
diff --git a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp
new file mode 100644
index 0000000..ac399e1
--- /dev/null
+++ b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/TLS/SecureTransport/SecureTransportContextFactory.h>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/TLS/SecureTransport/SecureTransportContext.h>
+
+namespace Swift {
+
+// Default to disabled revocation checking as SecureTransport API is missing
+// methods for detailed revocation checking configuration which are needed for
+// good UX.
+SecureTransportContextFactory::SecureTransportContextFactory() : checkCertificateRevocation_(false), disconnectOnCardRemoval_(true) {
+
+}
+
+SecureTransportContextFactory::~SecureTransportContextFactory() {
+
+}
+
+bool SecureTransportContextFactory::canCreate() const {
+ return true;
+}
+
+std::unique_ptr<TLSContext> SecureTransportContextFactory::createTLSContext(const TLSOptions& /* tlsOptions */, TLSContext::Mode mode) {
+ // TLS server mode is not supported for the SecureTransport backend yet.
+ assert(mode == TLSContext::Mode::Client);
+ return std::unique_ptr<TLSContext>(new SecureTransportContext(checkCertificateRevocation_));
+}
+
+void SecureTransportContextFactory::setCheckCertificateRevocation(bool b) {
+ checkCertificateRevocation_ = b;
+}
+
+void SecureTransportContextFactory::setDisconnectOnCardRemoval(bool b) {
+ disconnectOnCardRemoval_ = b;
+ if (disconnectOnCardRemoval_) {
+ SWIFT_LOG(warning) << "Smart cards have not been tested yet";
+ }
+}
+
+}
diff --git a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h
new file mode 100644
index 0000000..b86639a
--- /dev/null
+++ b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <Swiften/TLS/TLSContextFactory.h>
+
+namespace Swift {
+
+class SecureTransportContextFactory : public TLSContextFactory {
+ public:
+ SecureTransportContextFactory();
+ virtual ~SecureTransportContextFactory();
+
+ virtual bool canCreate() const;
+
+ virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode = TLSContext::Mode::Client);
+ virtual void setCheckCertificateRevocation(bool b);
+ virtual void setDisconnectOnCardRemoval(bool b);
+
+ private:
+ bool checkCertificateRevocation_;
+ bool disconnectOnCardRemoval_;
+};
+
+}
diff --git a/Swiften/TLS/ServerIdentityVerifier.cpp b/Swiften/TLS/ServerIdentityVerifier.cpp
index 02459b9..18ea2aa 100644
--- a/Swiften/TLS/ServerIdentityVerifier.cpp
+++ b/Swiften/TLS/ServerIdentityVerifier.cpp
@@ -1,88 +1,96 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <Swiften/TLS/ServerIdentityVerifier.h>
#include <boost/algorithm/string.hpp>
-#include <Swiften/Base/foreach.h>
#include <Swiften/IDN/IDNConverter.h>
namespace Swift {
-ServerIdentityVerifier::ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter) {
- domain = jid.getDomain();
- encodedDomain = idnConverter->getIDNAEncoded(domain);
+ServerIdentityVerifier::ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter, bool checkServer) : domainValid(false), checkServer_(checkServer) {
+ domain = jid.getDomain();
+ boost::optional<std::string> domainResult = idnConverter->getIDNAEncoded(domain);
+ if (!!domainResult) {
+ encodedDomain = *domainResult;
+ domainValid = true;
+ }
}
bool ServerIdentityVerifier::certificateVerifies(Certificate::ref certificate) {
- bool hasSAN = false;
+ bool hasSAN = false;
- if (certificate == NULL) {
- return false;
- }
- // DNS names
- std::vector<std::string> dnsNames = certificate->getDNSNames();
- foreach (const std::string& dnsName, dnsNames) {
- if (matchesDomain(dnsName)) {
- return true;
- }
- }
- hasSAN |= !dnsNames.empty();
+ if (certificate == nullptr) {
+ return false;
+ }
+ // DNS names
+ std::vector<std::string> dnsNames = certificate->getDNSNames();
+ for (const auto& dnsName : dnsNames) {
+ if (matchesDomain(dnsName)) {
+ return true;
+ }
+ }
+ hasSAN |= !dnsNames.empty();
- // SRV names
- std::vector<std::string> srvNames = certificate->getSRVNames();
- foreach (const std::string& srvName, srvNames) {
- // Only match SRV names that begin with the service; this isn't required per
- // spec, but we're being purist about this.
- if (boost::starts_with(srvName, "_xmpp-client.") && matchesDomain(srvName.substr(std::string("_xmpp-client.").size(), srvName.npos))) {
- return true;
- }
- }
- hasSAN |= !srvNames.empty();
+ std::string prefix = (checkServer_) ? "_xmpp-server." : "_xmpp-client.";
- // XmppAddr
- std::vector<std::string> xmppAddresses = certificate->getXMPPAddresses();
- foreach (const std::string& xmppAddress, xmppAddresses) {
- if (matchesAddress(xmppAddress)) {
- return true;
- }
- }
- hasSAN |= !xmppAddresses.empty();
+ // SRV names
+ std::vector<std::string> srvNames = certificate->getSRVNames();
+ for (const auto& srvName : srvNames) {
+ // Only match SRV names that begin with the service; this isn't required per
+ // spec, but we're being purist about this.
+ if (boost::starts_with(srvName, prefix) && matchesDomain(srvName.substr(prefix.size(), srvName.npos))) {
+ return true;
+ }
+ }
+ hasSAN |= !srvNames.empty();
- // CommonNames. Only check this if there was no SAN (according to spec).
- if (!hasSAN) {
- std::vector<std::string> commonNames = certificate->getCommonNames();
- foreach (const std::string& commonName, commonNames) {
- if (matchesDomain(commonName)) {
- return true;
- }
- }
- }
+ // XmppAddr
+ std::vector<std::string> xmppAddresses = certificate->getXMPPAddresses();
+ for (const auto& xmppAddress : xmppAddresses) {
+ if (matchesAddress(xmppAddress)) {
+ return true;
+ }
+ }
+ hasSAN |= !xmppAddresses.empty();
- return false;
+ // CommonNames. Only check this if there was no SAN (according to spec).
+ if (!hasSAN) {
+ std::vector<std::string> commonNames = certificate->getCommonNames();
+ for (const auto& commonName : commonNames) {
+ if (matchesDomain(commonName)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
}
bool ServerIdentityVerifier::matchesDomain(const std::string& s) const {
- if (boost::starts_with(s, "*.")) {
- std::string matchString(s.substr(2, s.npos));
- std::string matchDomain = encodedDomain;
- size_t dotIndex = matchDomain.find('.');
- if (dotIndex != matchDomain.npos) {
- matchDomain = matchDomain.substr(dotIndex + 1, matchDomain.npos);
- }
- return matchString == matchDomain;
- }
- else {
- return s == encodedDomain;
- }
+ if (!domainValid) {
+ return false;
+ }
+ if (boost::starts_with(s, "*.")) {
+ std::string matchString(s.substr(2, s.npos));
+ std::string matchDomain = encodedDomain;
+ size_t dotIndex = matchDomain.find('.');
+ if (dotIndex != matchDomain.npos) {
+ matchDomain = matchDomain.substr(dotIndex + 1, matchDomain.npos);
+ }
+ return boost::iequals(matchString, matchDomain);
+ }
+ else {
+ return boost::iequals(s, encodedDomain);
+ }
}
bool ServerIdentityVerifier::matchesAddress(const std::string& s) const {
- return s == domain;
+ return boost::iequals(s, domain);
}
}
diff --git a/Swiften/TLS/ServerIdentityVerifier.h b/Swiften/TLS/ServerIdentityVerifier.h
index 4167ce8..f2cf46f 100644
--- a/Swiften/TLS/ServerIdentityVerifier.h
+++ b/Swiften/TLS/ServerIdentityVerifier.h
@@ -1,33 +1,35 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <boost/shared_ptr.hpp>
-
+#include <memory>
#include <string>
+
#include <Swiften/Base/API.h>
#include <Swiften/JID/JID.h>
#include <Swiften/TLS/Certificate.h>
namespace Swift {
- class IDNConverter;
+ class IDNConverter;
- class SWIFTEN_API ServerIdentityVerifier {
- public:
- ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter);
+ class SWIFTEN_API ServerIdentityVerifier {
+ public:
+ ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter, bool checkServer=false);
- bool certificateVerifies(Certificate::ref);
+ bool certificateVerifies(Certificate::ref);
- private:
- bool matchesDomain(const std::string&) const ;
- bool matchesAddress(const std::string&) const;
+ private:
+ bool matchesDomain(const std::string&) const ;
+ bool matchesAddress(const std::string&) const;
- private:
- std::string domain;
- std::string encodedDomain;
- };
+ private:
+ std::string domain;
+ std::string encodedDomain;
+ bool domainValid;
+ bool checkServer_;
+ };
}
diff --git a/Swiften/TLS/SimpleCertificate.h b/Swiften/TLS/SimpleCertificate.h
index 4cf0cc2..08cf1e3 100644
--- a/Swiften/TLS/SimpleCertificate.h
+++ b/Swiften/TLS/SimpleCertificate.h
@@ -1,76 +1,78 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
#include <string>
+
+#include <Swiften/Base/API.h>
#include <Swiften/TLS/Certificate.h>
namespace Swift {
- class SimpleCertificate : public Certificate {
- public:
- typedef boost::shared_ptr<SimpleCertificate> ref;
-
- void setSubjectName(const std::string& name) {
- subjectName = name;
- }
-
- std::string getSubjectName() const {
- return subjectName;
- }
-
- std::vector<std::string> getCommonNames() const {
- return commonNames;
- }
-
- void addCommonName(const std::string& name) {
- commonNames.push_back(name);
- }
-
- void addSRVName(const std::string& name) {
- srvNames.push_back(name);
- }
-
- void addDNSName(const std::string& name) {
- dnsNames.push_back(name);
- }
-
- void addXMPPAddress(const std::string& addr) {
- xmppAddresses.push_back(addr);
- }
-
- std::vector<std::string> getSRVNames() const {
- return srvNames;
- }
-
- std::vector<std::string> getDNSNames() const {
- return dnsNames;
- }
-
- std::vector<std::string> getXMPPAddresses() const {
- return xmppAddresses;
- }
-
- ByteArray toDER() const {
- return der;
- }
-
- void setDER(const ByteArray& der) {
- this->der = der;
- }
-
- private:
- void parse();
-
- private:
- std::string subjectName;
- ByteArray der;
- std::vector<std::string> commonNames;
- std::vector<std::string> dnsNames;
- std::vector<std::string> xmppAddresses;
- std::vector<std::string> srvNames;
- };
+ class SWIFTEN_API SimpleCertificate : public Certificate {
+ public:
+ typedef std::shared_ptr<SimpleCertificate> ref;
+
+ void setSubjectName(const std::string& name) {
+ subjectName = name;
+ }
+
+ std::string getSubjectName() const {
+ return subjectName;
+ }
+
+ std::vector<std::string> getCommonNames() const {
+ return commonNames;
+ }
+
+ void addCommonName(const std::string& name) {
+ commonNames.push_back(name);
+ }
+
+ void addSRVName(const std::string& name) {
+ srvNames.push_back(name);
+ }
+
+ void addDNSName(const std::string& name) {
+ dnsNames.push_back(name);
+ }
+
+ void addXMPPAddress(const std::string& addr) {
+ xmppAddresses.push_back(addr);
+ }
+
+ std::vector<std::string> getSRVNames() const {
+ return srvNames;
+ }
+
+ std::vector<std::string> getDNSNames() const {
+ return dnsNames;
+ }
+
+ std::vector<std::string> getXMPPAddresses() const {
+ return xmppAddresses;
+ }
+
+ ByteArray toDER() const {
+ return der;
+ }
+
+ void setDER(const ByteArray& der) {
+ this->der = der;
+ }
+
+ private:
+ void parse();
+
+ private:
+ std::string subjectName;
+ ByteArray der;
+ std::vector<std::string> commonNames;
+ std::vector<std::string> dnsNames;
+ std::vector<std::string> xmppAddresses;
+ std::vector<std::string> srvNames;
+ };
}
diff --git a/Swiften/TLS/TLSContext.cpp b/Swiften/TLS/TLSContext.cpp
index d461d91..fd31c2d 100644
--- a/Swiften/TLS/TLSContext.cpp
+++ b/Swiften/TLS/TLSContext.cpp
@@ -1,19 +1,53 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <Swiften/TLS/TLSContext.h>
+#include <cassert>
+
namespace Swift {
TLSContext::~TLSContext() {
}
+void TLSContext::accept() {
+ assert(false);
+}
+
+void TLSContext::connect(const std::string& /* serverName */) {
+ assert(false);
+}
+
+bool TLSContext::setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& /* certificateChain */) {
+ assert(false);
+ return false;
+}
+
+bool TLSContext::setPrivateKey(const PrivateKey::ref& /* privateKey */) {
+ assert(false);
+ return false;
+}
+
+bool TLSContext::setDiffieHellmanParameters(const ByteArray& /*parametersInOpenSslDer*/) {
+ assert(false);
+ return false;
+}
+
+void TLSContext::setAbortTLSHandshake(bool /* abort */) {
+ assert(false);
+}
+
Certificate::ref TLSContext::getPeerCertificate() const {
- std::vector<Certificate::ref> chain = getPeerCertificateChain();
- return chain.empty() ? Certificate::ref() : chain[0];
+ std::vector<Certificate::ref> chain = getPeerCertificateChain();
+ return chain.empty() ? Certificate::ref() : chain[0];
+}
+
+ByteArray TLSContext::getPeerFinishMessage() const {
+ assert(false);
+ return ByteArray();
}
}
diff --git a/Swiften/TLS/TLSContext.h b/Swiften/TLS/TLSContext.h
index 5fee021..f2dbdce 100644
--- a/Swiften/TLS/TLSContext.h
+++ b/Swiften/TLS/TLSContext.h
@@ -1,43 +1,66 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <Swiften/Base/boost_bsignals.h>
-#include <boost/shared_ptr.hpp>
+#include <memory>
+#include <boost/signals2.hpp>
+
+#include <Swiften/Base/API.h>
#include <Swiften/Base/SafeByteArray.h>
#include <Swiften/TLS/Certificate.h>
-#include <Swiften/TLS/CertificateWithKey.h>
#include <Swiften/TLS/CertificateVerificationError.h>
+#include <Swiften/TLS/CertificateWithKey.h>
+#include <Swiften/TLS/PrivateKey.h>
#include <Swiften/TLS/TLSError.h>
namespace Swift {
- class TLSContext {
- public:
- virtual ~TLSContext();
+ class SWIFTEN_API TLSContext {
+ public:
+ virtual ~TLSContext();
+
+ virtual void accept();
+ virtual void connect() = 0;
+ virtual void connect(const std::string& serverName);
+
+ virtual bool setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& /* certificateChain */);
+ virtual bool setPrivateKey(const PrivateKey::ref& /* privateKey */);
+
+ virtual bool setClientCertificate(CertificateWithKey::ref cert) = 0;
+ virtual bool setDiffieHellmanParameters(const ByteArray& parametersInOpenSslDer);
- virtual void connect() = 0;
+ /**
+ * This method can be used during the \ref onServerNameRequested signal,
+ * to report an error about an unknown host back to the requesting client.
+ */
+ virtual void setAbortTLSHandshake(bool /* abort */);
- virtual bool setClientCertificate(CertificateWithKey::ref cert) = 0;
+ virtual void handleDataFromNetwork(const SafeByteArray&) = 0;
+ virtual void handleDataFromApplication(const SafeByteArray&) = 0;
- virtual void handleDataFromNetwork(const SafeByteArray&) = 0;
- virtual void handleDataFromApplication(const SafeByteArray&) = 0;
+ Certificate::ref getPeerCertificate() const;
+ virtual std::vector<Certificate::ref> getPeerCertificateChain() const = 0;
+ virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const = 0;
- Certificate::ref getPeerCertificate() const;
- virtual std::vector<Certificate::ref> getPeerCertificateChain() const = 0;
- virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const = 0;
+ virtual ByteArray getFinishMessage() const = 0;
+ virtual ByteArray getPeerFinishMessage() const;
- virtual ByteArray getFinishMessage() const = 0;
+ public:
+ enum class Mode {
+ Client,
+ Server
+ };
- public:
- boost::signal<void (const SafeByteArray&)> onDataForNetwork;
- boost::signal<void (const SafeByteArray&)> onDataForApplication;
- boost::signal<void (boost::shared_ptr<TLSError>)> onError;
- boost::signal<void ()> onConnected;
- };
+ public:
+ boost::signals2::signal<void (const SafeByteArray&)> onDataForNetwork;
+ boost::signals2::signal<void (const SafeByteArray&)> onDataForApplication;
+ boost::signals2::signal<void (std::shared_ptr<TLSError>)> onError;
+ boost::signals2::signal<void ()> onConnected;
+ boost::signals2::signal<void (const std::string&)> onServerNameRequested;
+ };
}
diff --git a/Swiften/TLS/TLSContextFactory.cpp b/Swiften/TLS/TLSContextFactory.cpp
index eb02f0c..91e60d6 100644
--- a/Swiften/TLS/TLSContextFactory.cpp
+++ b/Swiften/TLS/TLSContextFactory.cpp
@@ -1,14 +1,21 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#include <Swiften/TLS/TLSContextFactory.h>
+#include <cassert>
+
namespace Swift {
TLSContextFactory::~TLSContextFactory() {
}
+ByteArray TLSContextFactory::convertDHParametersFromPEMToDER(const std::string& /* pem */) {
+ assert(false);
+ return ByteArray();
+}
+
}
diff --git a/Swiften/TLS/TLSContextFactory.h b/Swiften/TLS/TLSContextFactory.h
index 5f08925..5f70541 100644
--- a/Swiften/TLS/TLSContextFactory.h
+++ b/Swiften/TLS/TLSContextFactory.h
@@ -1,21 +1,29 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
+#include <memory>
+
+#include <Swiften/Base/API.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/TLS/TLSContext.h>
+#include <Swiften/TLS/TLSOptions.h>
+
namespace Swift {
- class TLSContext;
+ class SWIFTEN_API TLSContextFactory {
+ public:
+ virtual ~TLSContextFactory();
- class TLSContextFactory {
- public:
- virtual ~TLSContextFactory();
+ virtual bool canCreate() const = 0;
- virtual bool canCreate() const = 0;
+ virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode = TLSContext::Mode::Client) = 0;
+ virtual void setCheckCertificateRevocation(bool b) = 0;
+ virtual void setDisconnectOnCardRemoval(bool b) = 0;
- virtual TLSContext* createTLSContext() = 0;
- virtual void setCheckCertificateRevocation(bool b) = 0;
- };
+ virtual ByteArray convertDHParametersFromPEMToDER(const std::string& pem);
+ };
}
diff --git a/Swiften/TLS/TLSError.h b/Swiften/TLS/TLSError.h
index f9ed8f7..9e4af2f 100644
--- a/Swiften/TLS/TLSError.h
+++ b/Swiften/TLS/TLSError.h
@@ -1,31 +1,41 @@
/*
- * Copyright (c) 2012 Kevin Smith
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2012-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
#pragma once
-#include <boost/shared_ptr.hpp>
+#include <memory>
+#include <string>
+
+#include <Swiften/Base/API.h>
#include <Swiften/Base/Error.h>
namespace Swift {
- class TLSError : public Error {
- public:
- typedef boost::shared_ptr<TLSError> ref;
+ class SWIFTEN_API TLSError : public Error {
+ public:
+ typedef std::shared_ptr<TLSError> ref;
+
+ enum Type {
+ UnknownError,
+ CertificateCardRemoved,
+ AcceptFailed,
+ ConnectFailed
+ };
- enum Type {
- UnknownError,
- CertificateCardRemoved
- };
+ TLSError(Type type = UnknownError, std::string message = "") : type_(type), message_(std::move(message)) {}
- TLSError(Type type = UnknownError) : type(type) {}
+ Type getType() const {
+ return type_;
+ }
- Type getType() const {
- return type;
- }
+ const std::string& getMessage() const {
+ return message_;
+ }
- private:
- Type type;
- };
+ private:
+ Type type_;
+ std::string message_;
+ };
}
diff --git a/Swiften/TLS/TLSOptions.h b/Swiften/TLS/TLSOptions.h
new file mode 100644
index 0000000..e3faaf9
--- /dev/null
+++ b/Swiften/TLS/TLSOptions.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+namespace Swift {
+ class TLSContext;
+ class Certificate;
+
+ class TLSOptions {
+ public:
+ TLSOptions() : schannelTLS1_0Workaround(false) {
+
+ }
+
+ /**
+ * A bug in the Windows SChannel TLS stack, combined with
+ * overly-restrictive server stacks means it's sometimes necessary to
+ * not use TLS>1.0. This option has no effect unless compiled on
+ * Windows against SChannel (OpenSSL users are unaffected).
+ */
+ bool schannelTLS1_0Workaround;
+
+ /**
+ * OpenSSL configuration flags
+ */
+ boost::optional<bool> workaroundMicrosoftSessID;
+ boost::optional<bool> workaroundNetscapeChallenge;
+ boost::optional<bool> workaroundNetscapeReuseCipherChange;
+ boost::optional<bool> workaroundSSLRef2ReuseCertType;
+ boost::optional<bool> workaroundMicrosoftBigSSLv3Buffer;
+ boost::optional<bool> workaroundSSLeay080ClientDH;
+ boost::optional<bool> workaroundTLSD5;
+ boost::optional<bool> workaroundTLSBlockPadding;
+ boost::optional<bool> workaroundDontInsertEmptyFragments;
+ boost::optional<bool> workaroundAll;
+ boost::optional<bool> suppressSSLv2;
+ boost::optional<bool> suppressSSLv3;
+ boost::optional<bool> suppressTLSv1;
+ boost::optional<bool> disableTLSRollBackBug;
+ boost::optional<bool> singleDHUse;
+
+ /**
+ * Other OpenSSL configuration items
+ */
+ boost::optional<std::string> cipherSuites;
+ boost::optional<std::string> context;
+ boost::optional<int> sessionCacheTimeout;
+ boost::optional<int> verifyDepth;
+
+ enum class VerifyMode {
+ None,
+ Required,
+ Optional
+ };
+ boost::optional<VerifyMode> verifyMode;
+
+ /**
+ * Callback for certificate verification
+ */
+
+ boost::optional<std::function<int(const TLSContext *)>> verifyCertificateCallback;
+
+ /**
+ * Allows specification of application-specific Trust Anchors
+ */
+ boost::optional<std::vector<std::shared_ptr<Certificate>>> trustAnchors;
+
+ /**
+ * Turns off automatic loading of system Trust Anchors
+ */
+ bool ignoreSystemTrustAnchors = false;
+ };
+}
diff --git a/Swiften/TLS/UnitTest/CertificateTest.cpp b/Swiften/TLS/UnitTest/CertificateTest.cpp
index 3352118..d5bf210 100644
--- a/Swiften/TLS/UnitTest/CertificateTest.cpp
+++ b/Swiften/TLS/UnitTest/CertificateTest.cpp
@@ -1,34 +1,143 @@
/*
- * Copyright (c) 2010-2013 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
-#include <Swiften/Base/ByteArray.h>
+#include <memory>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
-#include <boost/smart_ptr/make_shared.hpp>
-#include <Swiften/TLS/Certificate.h>
-#include <Swiften/TLS/SimpleCertificate.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/Platform.h>
#include <Swiften/Crypto/CryptoProvider.h>
#include <Swiften/Crypto/PlatformCryptoProvider.h>
+#include <Swiften/TLS/Certificate.h>
+#include <Swiften/TLS/CertificateFactory.h>
+#include <Swiften/TLS/PlatformTLSFactories.h>
+#include <Swiften/TLS/SimpleCertificate.h>
using namespace Swift;
class CertificateTest : public CppUnit::TestFixture {
- CPPUNIT_TEST_SUITE(CertificateTest);
- CPPUNIT_TEST(testGetSHA1Fingerprint);
- CPPUNIT_TEST_SUITE_END();
+ CPPUNIT_TEST_SUITE(CertificateTest);
+ CPPUNIT_TEST(testGetSHA1Fingerprint);
+#ifdef HAVE_OPENSSL
+ CPPUNIT_TEST(testParsingPEMCertificate);
+#endif
+ CPPUNIT_TEST(testParsingPEMPrivateKey);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testGetSHA1Fingerprint() {
+ SimpleCertificate::ref testling = std::make_shared<SimpleCertificate>();
+ testling->setDER(createByteArray("abcdefg"));
+
+ CPPUNIT_ASSERT_EQUAL(std::string("2f:b5:e1:34:19:fc:89:24:68:65:e7:a3:24:f4:76:ec:62:4e:87:40"), Certificate::getSHA1Fingerprint(testling, std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()).get()));
+ }
+
+ void testParsingPEMCertificate() {
+ PlatformTLSFactories tlsFactories;
+
+ auto PEMCertificate = R"(-----BEGIN CERTIFICATE-----
+MIIEsjCCApoCCQCbkjlQfUqPtTANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDDBBt
+b250YWd1ZS5leGFtcGxlMB4XDTE4MDExODE2NTMxMloXDTQ1MDYwNDE2NTMxMlow
+GzEZMBcGA1UEAwwQbW9udGFndWUuZXhhbXBsZTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBALAx5xuEYOjDJ9QHMvwRuDzxbHl1zR97SJFPAkmD8xH0sC61
+DNRyUvRq6UXb4znhqeqrNuZ9PV47GyK2Dpy/c/MY5NE3m/c+Z1tUnrcqyCkxITIn
+jdSI/elc9yhtMXX6lRi68BdPJnj/9+6trW0cWHlKEgnaSTAgCVB+4Dg9mjTIroCa
+HLoAKhr2zS7Ihs28aWb0cSvZ+qFgQhr7FGP2kfssTTQYyRPn9uHhtyWH6RqSv5x9
+BBGZWN5GtjkJvupcYWLt8ftuQyFpwEeEz5cgtiyKgsfz9CYvS9/OcwdLQr4z5Zq6
+eKxsog9GdwIi1Us4KR0X6tKu9ze42iEWF+foEWFP9/bjrVK/tt5lTSKnenk0nA8I
+amkG0btNAGihDti3lv60bGpd3B2/uh4gzzT2buXdf0EaybCt52MIr8xkwMU0Tkri
+RAYchdS8U8sekWG5Lg9m3L2BDa8/TKS/WflJhGcZeInGQslgIx7fCgO1M7Zr50pO
+wSURPXpvqUkXNEBy639UQEUsnBhntEQwZWx/6x6Ma/U5a5dL6qbtEJjlwIvS+nl9
+3w26g3DvWydNMCtZIVhgdrl+dZs+Uw5eA3QkHkDTSfYvQk7X5SYL0J5ZxwBvU9r1
+ED054+TAEuX2euiRA37xLhxonj8BaKkPQGlAHCLZaZPmNJWkNxElJhMoCfqBAgMB
+AAEwDQYJKoZIhvcNAQELBQADggIBAF+FNyW3nVeQeugBMksIhj7EMJl1AEKi0+78
+ZPsYX3CDbc/8GRZoTg/EWSiwPCBYc9VsxuKtODEYABCZgk7LnSBYoEauJDKWqkgM
+UOKPJI2hu7mIK7FJpjvEZe2MnRRA63oI/NVDJm8T2clrv/vPkY+ppsVl0toC0SpH
+/3dF5c65vYI19rTJraRU6kIrrgxFBzxzpn07LGh2rrOCQfy2umTSRMwz3ORAFfmi
++Kek1Dt7c+JVJ0ivCwhhc8MKza3JS2MuDfVWGnXtDLb81Ai0t4tQfLKvZEcgW+lh
+Drz9gv22buwncWL/IxtuhzyILtDSDKAYFbhfG6IAQut9BjMgpMnKrBCDlOLJl08K
+tgj2h7vUKyNSt3ndcSAtXjr6FD7+xPExJuyn/MgLONGGAZoZHFB4QO90wQaXxMPh
+7rnjUtzfLR8qkDmX8ZB4f4VOWpDWo4hBpgjTk0gYfzEkrh+zTqE9reh7CZ1WzwXO
+KnIBU2dZOE+XsJe49lW106DLqGzKRuQMUAwFMb7C7Nlg9GKTnvi41o+g6YE+MgxR
+uPKu891pCBtnDxZiWPT+7Fa/9UXxdIOTVIHW6utSWiWYbeNwXSmIm2ShfmNfWj9m
+x1JgJrFB6daWGR9aDBeDVRhgL6Z35lH7xI62pLJ4o2d2Y/9dUWAJfz5O8opeCyrF
+zqCzpwGL
+-----END CERTIFICATE-----
+)";
+
+ auto certificateChain = tlsFactories.getCertificateFactory()->createCertificateChain(createByteArray(PEMCertificate));
+ CPPUNIT_ASSERT_EQUAL(size_t(1), certificateChain.size());
+
+ auto certificate = certificateChain[0].get();
+ CPPUNIT_ASSERT_EQUAL(std::string("/CN=montague.example"), certificate->getSubjectName());
+ }
+
+ void testParsingPEMPrivateKey() {
+ PlatformTLSFactories tlsFactories;
+
+ auto PEMPrivateKey = R"(-----BEGIN PRIVATE KEY-----
+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
+5IMXuORWuoq0gXiCdEyko+saXsyWlqw=
+-----END PRIVATE KEY-----
+)";
- public:
- void testGetSHA1Fingerprint() {
- SimpleCertificate::ref testling = boost::make_shared<SimpleCertificate>();
- testling->setDER(createByteArray("abcdefg"));
+ auto privateKey = tlsFactories.getCertificateFactory()->createPrivateKey(createSafeByteArray(PEMPrivateKey));
- CPPUNIT_ASSERT_EQUAL(std::string("2f:b5:e1:34:19:fc:89:24:68:65:e7:a3:24:f4:76:ec:62:4e:87:40"), Certificate::getSHA1Fingerprint(testling, boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()).get()));
- }
+ CPPUNIT_ASSERT(privateKey != nullptr);
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(CertificateTest);
diff --git a/Swiften/TLS/UnitTest/ClientServerTest.cpp b/Swiften/TLS/UnitTest/ClientServerTest.cpp
new file mode 100644
index 0000000..a356dcf
--- /dev/null
+++ b/Swiften/TLS/UnitTest/ClientServerTest.cpp
@@ -0,0 +1,883 @@
+/*
+ * Copyright (c) 2010-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <boost/variant.hpp>
+
+#include <gtest/gtest.h>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/TLS/CertificateFactory.h>
+#include <Swiften/TLS/PlatformTLSFactories.h>
+#include <Swiften/TLS/TLSContext.h>
+#include <Swiften/TLS/TLSContextFactory.h>
+#include <Swiften/TLS/TLSOptions.h>
+
+using namespace Swift;
+namespace {
+
+
+std::map<std::string, std::string> certificatePEM = {
+ {"montague.example",
+R"(-----BEGIN CERTIFICATE-----
+MIIEsjCCApoCCQCbkjlQfUqPtTANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDDBBt
+b250YWd1ZS5leGFtcGxlMB4XDTE4MDExODE2NTMxMloXDTQ1MDYwNDE2NTMxMlow
+GzEZMBcGA1UEAwwQbW9udGFndWUuZXhhbXBsZTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBALAx5xuEYOjDJ9QHMvwRuDzxbHl1zR97SJFPAkmD8xH0sC61
+DNRyUvRq6UXb4znhqeqrNuZ9PV47GyK2Dpy/c/MY5NE3m/c+Z1tUnrcqyCkxITIn
+jdSI/elc9yhtMXX6lRi68BdPJnj/9+6trW0cWHlKEgnaSTAgCVB+4Dg9mjTIroCa
+HLoAKhr2zS7Ihs28aWb0cSvZ+qFgQhr7FGP2kfssTTQYyRPn9uHhtyWH6RqSv5x9
+BBGZWN5GtjkJvupcYWLt8ftuQyFpwEeEz5cgtiyKgsfz9CYvS9/OcwdLQr4z5Zq6
+eKxsog9GdwIi1Us4KR0X6tKu9ze42iEWF+foEWFP9/bjrVK/tt5lTSKnenk0nA8I
+amkG0btNAGihDti3lv60bGpd3B2/uh4gzzT2buXdf0EaybCt52MIr8xkwMU0Tkri
+RAYchdS8U8sekWG5Lg9m3L2BDa8/TKS/WflJhGcZeInGQslgIx7fCgO1M7Zr50pO
+wSURPXpvqUkXNEBy639UQEUsnBhntEQwZWx/6x6Ma/U5a5dL6qbtEJjlwIvS+nl9
+3w26g3DvWydNMCtZIVhgdrl+dZs+Uw5eA3QkHkDTSfYvQk7X5SYL0J5ZxwBvU9r1
+ED054+TAEuX2euiRA37xLhxonj8BaKkPQGlAHCLZaZPmNJWkNxElJhMoCfqBAgMB
+AAEwDQYJKoZIhvcNAQELBQADggIBAF+FNyW3nVeQeugBMksIhj7EMJl1AEKi0+78
+ZPsYX3CDbc/8GRZoTg/EWSiwPCBYc9VsxuKtODEYABCZgk7LnSBYoEauJDKWqkgM
+UOKPJI2hu7mIK7FJpjvEZe2MnRRA63oI/NVDJm8T2clrv/vPkY+ppsVl0toC0SpH
+/3dF5c65vYI19rTJraRU6kIrrgxFBzxzpn07LGh2rrOCQfy2umTSRMwz3ORAFfmi
++Kek1Dt7c+JVJ0ivCwhhc8MKza3JS2MuDfVWGnXtDLb81Ai0t4tQfLKvZEcgW+lh
+Drz9gv22buwncWL/IxtuhzyILtDSDKAYFbhfG6IAQut9BjMgpMnKrBCDlOLJl08K
+tgj2h7vUKyNSt3ndcSAtXjr6FD7+xPExJuyn/MgLONGGAZoZHFB4QO90wQaXxMPh
+7rnjUtzfLR8qkDmX8ZB4f4VOWpDWo4hBpgjTk0gYfzEkrh+zTqE9reh7CZ1WzwXO
+KnIBU2dZOE+XsJe49lW106DLqGzKRuQMUAwFMb7C7Nlg9GKTnvi41o+g6YE+MgxR
+uPKu891pCBtnDxZiWPT+7Fa/9UXxdIOTVIHW6utSWiWYbeNwXSmIm2ShfmNfWj9m
+x1JgJrFB6daWGR9aDBeDVRhgL6Z35lH7xI62pLJ4o2d2Y/9dUWAJfz5O8opeCyrF
+zqCzpwGL
+-----END CERTIFICATE-----
+)"},
+ {"capulet.example",
+R"(-----BEGIN CERTIFICATE-----
+MIIEsDCCApgCCQDUGdmqHfGngTANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9j
+YXB1bGV0LmV4YW1wbGUwHhcNMTgwMTE4MTY1NjEyWhcNNDUwNjA0MTY1NjEyWjAa
+MRgwFgYDVQQDDA9jYXB1bGV0LmV4YW1wbGUwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCgA/CpV7UGqUdjhEVoMjFIZ6Ca/C7utrVPAqBvE14eiurIhQrQ
+AmYeC9zA4/uXCdqkGq/a/RbF3OOCKLRUejCcynb0EnxiHxHa48ZsvLbKCK6guoXE
+pWnaZsmRpvJrBB2z6ohmxRuaDuc5CJT+Oq8AFPp3StTAFDo3Cju3fsGZPkNpCGbj
+ELwk4ok9INtEuTrMEmHZTD+VfjpXauUfN4ygKaPXJanCuxuifyin403BWgd8igkD
+oSCWxoDFMy3HGbh/NU+nJPJ23FxVC39RdDG3elvjNFpSZnALbiMnyor7YjF5TN93
+1ZWwn3VTnl7DnFquEbKbYyVFUzRII8Cd1TzKOL48rVLrCAqMXwm6YFtST6qPb9TZ
+0SX8qZGCwBfHV5OeS0ZgiBMMlmUPYcw9MlyvZnYyDPCOoPWmhEqd2gQzn//7hzb5
+mriCEyfcMzAqohylBNHXUVZTx5KcazJz6oOYdWEs1jfSXNKefgSWlgeoG2fgTXPN
+1OkQVS+FOiI0VCAIwR+vxhG3hVTz3kzXWvEt7M51faaHWWlnSzOrbSuj3f0ibS5J
+cj6ClyzOQRQDwzofyZ7oPWh6No/XkepVIn3HTTlnj1/8e6VsH+EBKSzoX2XvWPkO
+GAZEGHKiKh944u6d6mW37BPD2oKyusP3uPL5j2Fdm+m0HkP3/7yw+5EFVQIDAQAB
+MA0GCSqGSIb3DQEBCwUAA4ICAQCfCGK4iDo8H0i12wBC0+6GB9NBmgn8P09AUN0l
+tQjZPaqZCtLtBBqOrAaZQBQMfI0QAm5/h/VkhMvz5HQjqmQhI2drSDOmarYzOGgY
+yVItkEwtLEVhiGoeJ+L3Smosoqq6QFjcDzH7UzPTrJNspnY+09+m53pJggh41zzt
+1TOU+QasaB5oGopFaFUUlwjHAc7FpZgEd+tV6j0cpdT3GabVkkoLg01Z+0mqkpLD
+OjRBKJX8XvZ38VESsy3gWpcXnDq03n+8OgZo4R9SEcyyxjWbyb+qg2dzbQvRB2Va
+QNoXp5EzemXvFSulhR+TfDk2K1h45BurikRQxDi8LpBTUsCMwiqXdem68HOlTwLi
+/kMWbnVBcdurYcWVSwlJU4EJcTEdk51JStO1V0nAA0nCwn/iEhY8I6BitnrcCJ5e
+4CGVWr+zAm8DBjaFMTzy46Q5NcT0hwnHGN6T6l4aMcRggIK9anRbXCn6dSzma1pd
+R5N/Do00FTpyZGcUlVPnSlIfZVl7y/9XEO1n6xDJURrefL1JrM7UMyB17jA8HMq3
+S05kF7XRpludRB4QkAJt5BNNv6BPP7HPIKyR/rq94ONvzVPAo7uASyFE2sMBsfwP
+pXAI1LVolPCoUC13jEkKdmc8kMSxU+XtsvFryNhkfQtZfSg+nBRFYptFE7GrZ9WY
+GMSL4g==
+-----END CERTIFICATE-----
+)"},
+ {"casigned.example.com",
+R"(-----BEGIN CERTIFICATE-----
+MIIDYTCCAkmgAwIBAgIJAPEpYlUWlJW8MA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV
+BAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ4wDAYDVQQKDAVJc29kZTEPMA0GA1UE
+AwwGVGVzdENBMB4XDTE4MDExODAwMDAwMFoXDTQ5MDExODAwMDAwMFowTjELMAkG
+A1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxDjAMBgNVBAoMBUlzb2RlMR0wGwYD
+VQQDDBRjYXNpZ25lZC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAKw4iCscGIDCc55mwxgJsn/v2GEorpE9zHYsMfl6px2aRLGgB8ki
+xYyNiEPA/b/ilShRp7pp22LPb5O3aOVVWzAs5FTvPdFY2vS8YIxR6XSKXO3u4Q8/
+w8CR0AbKpeCtL0TwSl1u76nq6ORMep6QGsTjDLQ7wUwdShL4qV4nIGxJWon/5GI0
+nl4Xr/wzmysbMjAMSiWOR90DNZMvqQBBhQcJdqdirc+whR8gOhIMvJBn5jlMuFEB
+zL/bR+kG/zFuD0EMsMTy6TETefYuxeWmhuzQENyAV5+1v48G/1a9zzN9Y0xQ6T0N
+ppbLzq+/zhxT1eBN8/O0bAGUzzk1VSFvouECAwEAAaNQME4wHQYDVR0OBBYEFCnR
+umlxjdaUvcpf77zahrDmPnt1MB8GA1UdIwQYMBaAFAbbhPAKtD5YzxQqGH2Um4/f
+OmdFMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEPrTTKS+7A4dJJh
+gSAS44ZYyR8/kt9Y3V4qxJM2xrOrW6rM8N15n2i/osSy0AYHiPuvgDG+D7hLt5Ct
+7jZKrp3JRsBaU8//9k6+euwVyWS+EKo/eb7qtSNibZOk7GwrXekJMbRXz1cHTKnW
+0R8UG+EMkOqKQh5dhWuEBXff9SYLTALf0/i+/Ixl3b9rQ6zpY/7UwBdlWLaliM1i
+odIgVu1XRE6Su+bGMa1L5ArniN0LMFq9Mxag2H0V7Kru7vIvbBkL7U6tbi7u26hv
+q4kUFROW2U2C02FTmJeSEBvWoDyMoVjKkmaTL0+vDSQRPGtCsvCgaCXgOwU2Po9s
+8jjHQxg=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDUzCCAjugAwIBAgIJAPEpYlUWlJW7MA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV
+BAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ4wDAYDVQQKDAVJc29kZTEPMA0GA1UE
+AwwGVGVzdENBMB4XDTE4MDExODAwMDAwMFoXDTQ5MDExODAwMDAwMFowQDELMAkG
+A1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxDjAMBgNVBAoMBUlzb2RlMQ8wDQYD
+VQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiCA+9
+bdSHFNPOgbsnhrKbLJP+1lxWTe06+ExwJA3QgCKsR13MsAK8rhmuNB0k8ZIFJL+c
+yUOUgw0370eP940QAtK+HinJpvaGEZKEwaB8VUCjzFTb7zUBio1Y7B45aawEV3qT
+Lzcnil3f7vPIxnAoaptzPriySdggIsimj+y6AWum5FUPyfZzF7EnYfF/VH8V/ZNX
+fku7PPbaeDHvu4EF/0s8P+/l6jddz8dqbDMjH3GDM8sjryWPQPNDjKZ1x80BIdyq
+s9m/kXqIIySNwdIa/X+nYTMchUa5y46n2N4n0RA4F+rf+Ni1Cxk9Ejmmz5hinV+t
+Yzh6jsbB7yZiqEnzAgMBAAGjUDBOMB0GA1UdDgQWBBQG24TwCrQ+WM8UKhh9lJuP
+3zpnRTAfBgNVHSMEGDAWgBQG24TwCrQ+WM8UKhh9lJuP3zpnRTAMBgNVHRMEBTAD
+AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBMHQ3WMEhkEc0VoOEw3mbsngt1eyJz+nFM
+FeGdRb7JSwpJfe/yBxyGodGyHsQJPVole6sUdq8S1QGT547PkgDHc6Btoq96Mahq
+brgfx1JL9a1F8qaqcENebHc7ltW8kps4xtdlithfo4nKaciYZ5mKbug+AFv569mb
+LU3F282dyW1dLa7+L8dGbb8Ntpnp0BB0Cotm4GX3Xi6Y+fikouqcmXj7vX+uMhG4
+pQpXqb2ML1Gev4w6XUsnls+OJok97x99NsOcqtwqsLJS/sg0cpwrgZ9+50hQKlHO
+8H6hhNjGBkvpIiNsWb9UOc+Id86J+drs9Ed93Eyyvwnq2XcqPGF3
+-----END CERTIFICATE-----)"}};
+std::map<std::string, std::string> privateKeyPEM = {
+ {"montague.example",
+R"(-----BEGIN PRIVATE KEY-----
+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
+5IMXuORWuoq0gXiCdEyko+saXsyWlqw=
+-----END PRIVATE KEY-----
+)"},{"capulet.example",
+R"(-----BEGIN PRIVATE KEY-----
+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
+dwiNVl130YWaJLjiA9Poc2llKtypfQ8=
+-----END PRIVATE KEY-----
+)"},
+ {"casigned.example.com",
+R"(-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsOIgrHBiAwnOe
+ZsMYCbJ/79hhKK6RPcx2LDH5eqcdmkSxoAfJIsWMjYhDwP2/4pUoUae6adtiz2+T
+t2jlVVswLORU7z3RWNr0vGCMUel0ilzt7uEPP8PAkdAGyqXgrS9E8Epdbu+p6ujk
+THqekBrE4wy0O8FMHUoS+KleJyBsSVqJ/+RiNJ5eF6/8M5srGzIwDEoljkfdAzWT
+L6kAQYUHCXanYq3PsIUfIDoSDLyQZ+Y5TLhRAcy/20fpBv8xbg9BDLDE8ukxE3n2
+LsXlpobs0BDcgFeftb+PBv9Wvc8zfWNMUOk9DaaWy86vv84cU9XgTfPztGwBlM85
+NVUhb6LhAgMBAAECggEAP5KesUstwFoHU/GbEsSBn7hpp3Pc/MBTOUwXJoCgL81C
+jk8EkLrFMZ0NL/WNtcduR/PcdnOdCFyKU3zgu8dGYgIwRyoJJ74lFTJ0cVfLVbm9
+lBcZ/QcMu4kXwWoM0WMcj9YjnVnWHmMYlS2mFeIb/5HQnrdwIyisr1QbMSZAUbgn
+HOLSjkd0KUcXxwP9HfE/8zUFbUsv/wPHE9T5eWqZz7lKZRvgUyk3jYElpewxqPvR
+HPOq9BN87ft7rPzmmZ4JCU2tM5MEQONo3jT5pljTU/iz4pfs1HArHS+/zZbGcoiu
+AhODErhLucodwZ07MZsE9KAdGPGUeob4SgMwmkIiAQKBgQDYZ33DHOgYd5Fa7Tqv
+TC77Nbpjmnk5lyoInNf+7pxBTgqIuKbpS6Ctq76nHI0qerNfd44LqojQEJ9S5rbL
+3s+HR+pypx3DyysE92uLaVsF4rfwZeEpJyQ1Q9NGbsKVb2nNrdGs3LZhG5PL+JQT
+8lS9h/sMIbh5Gd/Dkt8a2HlTawKBgQDLu3YdXWeWUOcq63YrLFDO+RUGsCe1cf+2
+3wTmMRgxfzpAPDZKymUPwrJNd1ddCQiRVKg91pLhlDl8B89Q4iWdOpYnE6vrnw3w
+6iEdEyA6IauvEvD3JPXHGtVHKBS/wz06wQVGzsgMvVun1w32SDzTGZ/somvkN1UE
+BTMRVqXB4wKBgCgkgnq4GQG45FjGmtU7v5dP7yRc2lHpjZ2NYq/imU+v51pXTncG
+VWcB7drX/AO+QRnbdpkJaFmYS8W4EH6e9UY1+/KKt4XDYVaJgwSYZY8g8FgCvhLB
+NQFAdKejEitgQHMr5DI5uwcmc3MpyGYu1LNXzyT/+FVWjGCeFQ7IyzDDAoGAXK9I
++ez/bkQaFs2OMe/0R2TL6wIC3qohMpndSbf2ELaX0pdR/856EaR6p4FujlXIvw0r
+iQ5wPg3H5SRMbbikchYwL1DkusnedsadD/QeijO9PmW4mEcJRW5wrvGOFvuEhJFB
+jEgQfLM82muc33lXqpYSyn3N5cJCfBS9edrYiA8CgYEA0Tsa4KvwzmBXzy2jexUT
+PPYtV9S7hS0D7Owf5SBI1wtWczOu3Y6DsCG4mbDi/tvo1dxC9d5Ns8RXHH9mtdyn
+8VUuSu24lk7VZ0OP5/byiAGqC2urDFGe0iBqePdb+3uS1g132LzAJm4RqBYw4Xjd
+P6n9b0V3iodcFLMTy1ShJ+I=
+-----END PRIVATE KEY-----
+)"}};
+
+auto montagueEncryptedPEM = R"(-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,6B0A69362920334824667C1B6207279B
+
+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
+-----END RSA PRIVATE KEY-----
+)";
+
+auto dhParamsOpenSslDer1024 = R"(-----BEGIN DH PARAMETERS-----
+MIGHAoGBANjw4f5+gu8b8X8O6ALyJA1tH9TQTWZEI3YjUKQ1m0dVBMJ6XDC7FLJn
+gqE4hIGcm1FAWwIiuo0uMufqyVwFT2c+G8j4JHWK5z1tEP+GaqiO34N0cUo86qHp
+THSkJN7LuHbYRqI9fHWDZocW/5yAsDq5RPUCjFZAoh1BWdfDFfrjAgEC
+-----END DH PARAMETERS-----
+)";
+auto dhParamsOpenSslDer2048 = R"(-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA0Q6vD5qtrh3XEkVkbN29ord/k3sgo2Q3PiFmyFt89qqVbebEzYmt
+t8DwaFGMcGlyKs4Lb1s7vocm9y3M9C0FZm85Muvv8WCbLZVZ+wfEtMibOjgRoDqt
+p7Qqe7/iPgMVrSjWegVkg3V8K8dnPpohPClM0yOe4NpBjSVNgBVJRpEtH8gFiCor
+H7hw63HpN/MgFdkjZNeCN+erv8p673xH8LrN98gQpkdQ9vCqYt1dHxF2XZcxBp8x
+XganwPeGgQosofkA6nVB70hyjwjEyxnHJZIMlx6DPXWC7X6ed0SazgH0sQNdACvG
+uU1zHCVIv6/f0adKrJg0s1jrM3qWZ6HmUwIBAg==
+-----END DH PARAMETERS-----
+)";
+
+auto createTLSContext = [](TLSContext::Mode mode) {
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ auto tlsContextFactory = tlsFactories->getTLSContextFactory();
+
+ auto tlsContext = std::unique_ptr<TLSContext>(tlsContextFactory->createTLSContext({}, mode));
+ return tlsContext;
+};
+
+// This connects a TLSContext to another TLSContext
+class ClientServerConnector {
+ public:
+ ClientServerConnector(TLSContext* client, TLSContext* server) : clientContext_(client), serverContext_(server) {
+ connections_.push_back(clientContext_->onDataForNetwork.connect([&](const SafeByteArray& data) {
+ serverContext_->handleDataFromNetwork(data);
+ }));
+ connections_.push_back(serverContext_->onDataForNetwork.connect([&](const SafeByteArray& data) {
+ clientContext_->handleDataFromNetwork(data);
+ }));
+ }
+
+ private:
+ TLSContext* clientContext_;
+ TLSContext* serverContext_;
+ std::vector<boost::signals2::connection> connections_;
+};
+
+struct TLSDataForNetwork {
+ SafeByteArray data;
+};
+
+struct TLSDataForApplication {
+ SafeByteArray data;
+};
+
+struct TLSFault {
+ std::shared_ptr<Swift::TLSError> error;
+};
+
+struct TLSConnected {
+ std::vector<Certificate::ref> chain;
+};
+
+struct TLSServerNameRequested {
+ std::string name;
+};
+
+using TLSEvent = boost::variant<TLSDataForNetwork, TLSDataForApplication, TLSFault, TLSConnected, TLSServerNameRequested>;
+
+class TLSEventToSafeByteArrayVisitor : public boost::static_visitor<SafeByteArray> {
+ public:
+ SafeByteArray operator()(const TLSDataForNetwork& tlsData) const {
+ return tlsData.data;
+ }
+
+ SafeByteArray operator()(const TLSDataForApplication& tlsData) const {
+ return tlsData.data;
+ }
+
+ SafeByteArray operator()(const TLSFault&) const {
+ return createSafeByteArray("");
+ }
+
+ SafeByteArray operator()(const TLSConnected&) const {
+ return createSafeByteArray("");
+ }
+
+ SafeByteArray operator()(const TLSServerNameRequested&) const {
+ return createSafeByteArray("");
+ }
+};
+
+class TLSEventToStringVisitor : public boost::static_visitor<std::string> {
+ public:
+ std::string operator()(const TLSDataForNetwork& event) const {
+ return std::string("TLSDataForNetwork(") + "size: " + std::to_string(event.data.size()) + ")";
+ }
+
+ std::string operator()(const TLSDataForApplication& event) const {
+ return std::string("TLSDataForApplication(") + "size: " + std::to_string(event.data.size()) + ")";
+ }
+
+ std::string operator()(const TLSFault&) const {
+ return "TLSFault()";
+ }
+
+ std::string operator()(const TLSConnected& event) const {
+ std::string certificates;
+ for (auto cert : event.chain) {
+ certificates += "\t" + cert->getSubjectName() + "\n";
+ }
+ return std::string("TLSConnected()") + "\n" + certificates;
+ }
+
+ std::string operator()(const TLSServerNameRequested& event) const {
+ return std::string("TLSServerNameRequested(") + "name: " + event.name + ")";
+ }
+};
+
+class TLSClientServerEventHistory {
+ public:
+ TLSClientServerEventHistory(TLSContext* client, TLSContext* server) {
+ connectContext(std::string("client"), client);
+ connectContext(std::string("server"), server);
+ }
+
+ __attribute__((unused))
+ void print() {
+ auto count = 0;
+ std::cout << "\n";
+ for (auto event : events) {
+ if (event.first == "server") {
+ std::cout << std::string(80, ' ');
+ }
+ std::cout << count << ". ";
+ std::cout << event.first << " : " << boost::apply_visitor(TLSEventToStringVisitor(), event.second) << std::endl;
+ count++;
+ }
+ }
+
+ template<class TLSEventType>
+ boost::optional<TLSEventType> getEvent(const std::string& peer, size_t number = 0) {
+ for (const auto& pair : events) {
+ if (pair.first == peer) {
+ if (pair.second.type() == typeid(TLSEventType)) {
+ if (number == 0) {
+ return boost::optional<TLSEventType>(boost::get<TLSEventType>(pair.second));
+ }
+ else {
+ number--;
+ }
+ }
+ }
+ }
+ return {};
+ }
+
+ private:
+ void connectContext(const std::string& name, TLSContext* context) {
+ connections_.push_back(context->onDataForNetwork.connect([=](const SafeByteArray& data) {
+ events.push_back(std::pair<std::string, TLSEvent>(name, TLSDataForNetwork{data}));
+ }));
+ connections_.push_back(context->onDataForApplication.connect([=](const SafeByteArray& data) {
+ events.push_back(std::pair<std::string, TLSEvent>(name, TLSDataForApplication{data}));
+ }));
+ connections_.push_back(context->onError.connect([=](std::shared_ptr<Swift::TLSError> error) {
+ events.push_back(std::pair<std::string, TLSEvent>(name, TLSFault{error}));
+ }));
+ connections_.push_back(context->onConnected.connect([=](){
+ events.push_back(std::pair<std::string, TLSEvent>(name, TLSConnected{context->getPeerCertificateChain()}));
+ }));
+ }
+
+ public:
+ std::vector<std::pair<std::string, TLSEvent>> events;
+
+ private:
+ std::vector<boost::signals2::connection> connections_;
+};
+
+}
+
+TEST(ClientServerTest, testInitAndFreeContext) {
+ auto tlsClientContext = createTLSContext(TLSContext::Mode::Client);
+ auto tlsServerContext = createTLSContext(TLSContext::Mode::Server);
+}
+
+TEST(ClientServerTest, testServerSetPrivateKey) {
+ auto tlsServerContext = createTLSContext(TLSContext::Mode::Server);
+}
+
+TEST(ClientServerTest, testServerSetCertificateChain) {
+ auto tlsServerContext = createTLSContext(TLSContext::Mode::Server);
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationWithCertificateMissing) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ ASSERT_EQ("server", events.events[1].first);
+ ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second));
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationWithPrivateKeyMissing) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ ASSERT_EQ("server", events.events[1].first);
+ ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second));
+}
+
+TEST(ClientServerTest, testWrongPrivateKeyAfterCertificate) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["montague.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(false, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+}
+
+TEST(ClientServerTest, testWrongCertificateAfterPrivateKey) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["montague.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ ASSERT_EQ("server", events.events[1].first);
+ ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second));
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunication) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server");
+ ASSERT_EQ(true, firstMessageFromClient.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data));
+ auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client");
+ ASSERT_EQ(true, firstMessageFromServer.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data));
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationEncryptedPrivateKeyRightPassword) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["montague.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(montagueEncryptedPEM), createSafeByteArray("test"));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server");
+ ASSERT_EQ(true, firstMessageFromClient.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data));
+ auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client");
+ ASSERT_EQ(true, firstMessageFromServer.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data));
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationWithChainedCert) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_TRUE(serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["casigned.example.com"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["casigned.example.com"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_TRUE(serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto serverEvent = std::find_if(events.events.begin(), events.events.end(), [](std::pair<std::string, TLSEvent>& event){
+ return event.first == "server" && (event.second.type() == typeid(TLSDataForApplication));
+ });
+ ASSERT_NE(events.events.end(), serverEvent);
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(boost::apply_visitor(TLSEventToSafeByteArrayVisitor(), serverEvent->second)));
+ auto clientEvent = std::find_if(events.events.begin(), events.events.end(), [](std::pair<std::string, TLSEvent>& event){
+ return event.first == "client" && (event.second.type() == typeid(TLSDataForApplication));
+ });
+ ASSERT_NE(events.events.end(), clientEvent);
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(boost::apply_visitor(TLSEventToSafeByteArrayVisitor(), clientEvent->second)));
+}
+
+TEST(ClientServerTest, testSettingPrivateKeyWithWrongPassword) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["montague.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(montagueEncryptedPEM), createSafeByteArray("foo"));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(false, serverContext->setPrivateKey(privateKey));
+}
+
+TEST(ClientServerTest, testSettingPrivateKeyWithoutRequiredPassword) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["montague.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(montagueEncryptedPEM));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(false, serverContext->setPrivateKey(privateKey));
+}
+
+TEST(ClientServerTest, testClientServerSNIRequestedHostAvailable) {
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ serverContext->onServerNameRequested.connect([&](const std::string& requestedName) {
+ if (certificatePEM.find(requestedName) != certificatePEM.end() && privateKeyPEM.find(requestedName) != privateKeyPEM.end()) {
+ auto certChain = tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM[requestedName]));
+ ASSERT_EQ(true, serverContext->setCertificateChain(std::move(certChain)));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM[requestedName]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+ }
+ });
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect("montague.example");
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server");
+ ASSERT_EQ(true, firstMessageFromClient.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data));
+ auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client");
+ ASSERT_EQ(true, firstMessageFromServer.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data));
+
+ ASSERT_EQ("/CN=montague.example", events.getEvent<TLSConnected>("client")->chain[0]->getSubjectName());
+}
+
+TEST(ClientServerTest, testClientServerSNIRequestedHostUnavailable) {
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ serverContext->onServerNameRequested.connect([&](const std::string&) {
+ serverContext->setAbortTLSHandshake(true);
+ });
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect("montague.example");
+
+ ASSERT_EQ("server", events.events[1].first);
+ ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second));
+
+ ASSERT_EQ("client", events.events[3].first);
+ ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[3].second));
+}
+
+TEST(ClientServerTest, testClientServerEqualFinishedMessage) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ ASSERT_EQ(serverContext->getPeerFinishMessage(), clientContext->getFinishMessage());
+ ASSERT_EQ(clientContext->getPeerFinishMessage(), serverContext->getFinishMessage());
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationWith2048BitDHParams) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ ASSERT_EQ(true, serverContext->setDiffieHellmanParameters(tlsFactories->getTLSContextFactory()->convertDHParametersFromPEMToDER(dhParamsOpenSslDer2048)));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server");
+ ASSERT_EQ(true, firstMessageFromClient.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data));
+ auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client");
+ ASSERT_EQ(true, firstMessageFromServer.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data));
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationWith1024BitDHParams) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ ASSERT_EQ(true, serverContext->setDiffieHellmanParameters(tlsFactories->getTLSContextFactory()->convertDHParametersFromPEMToDER(dhParamsOpenSslDer1024)));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server");
+ ASSERT_EQ(true, firstMessageFromClient.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data));
+ auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client");
+ ASSERT_EQ(true, firstMessageFromServer.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data));
+}
diff --git a/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp b/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp
index e974eb7..47f3db2 100644
--- a/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp
+++ b/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp
@@ -1,178 +1,230 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2010-2016 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
*/
-#include <Swiften/Base/ByteArray.h>
+#include <vector>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
-#include <vector>
-#include <Swiften/TLS/ServerIdentityVerifier.h>
-#include <Swiften/TLS/SimpleCertificate.h>
+#include <Swiften/Base/ByteArray.h>
#include <Swiften/IDN/IDNConverter.h>
#include <Swiften/IDN/PlatformIDNConverter.h>
+#include <Swiften/TLS/ServerIdentityVerifier.h>
+#include <Swiften/TLS/SimpleCertificate.h>
using namespace Swift;
class ServerIdentityVerifierTest : public CppUnit::TestFixture {
- CPPUNIT_TEST_SUITE(ServerIdentityVerifierTest);
- CPPUNIT_TEST(testCertificateVerifies_WithoutMatchingDNSName);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingDNSName);
- CPPUNIT_TEST(testCertificateVerifies_WithSecondMatchingDNSName);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingInternationalDNSName);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingDNSNameWithWildcard);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingDNSNameWithWildcardMatchingNoComponents);
- CPPUNIT_TEST(testCertificateVerifies_WithDNSNameWithWildcardMatchingTwoComponents);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithoutService);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithService);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithServiceAndWildcard);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithDifferentService);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingXmppAddr);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingXmppAddrWithWildcard);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingInternationalXmppAddr);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingCNWithoutSAN);
- CPPUNIT_TEST(testCertificateVerifies_WithMatchingCNWithSAN);
- CPPUNIT_TEST_SUITE_END();
-
- public:
- void setUp() {
- idnConverter = boost::shared_ptr<IDNConverter>(PlatformIDNConverter::create());
- }
-
- void testCertificateVerifies_WithoutMatchingDNSName() {
- ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addDNSName("foo.com");
-
- CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithMatchingDNSName() {
- ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addDNSName("bar.com");
-
- CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithSecondMatchingDNSName() {
- ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addDNSName("foo.com");
- certificate->addDNSName("bar.com");
-
- CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithMatchingInternationalDNSName() {
- ServerIdentityVerifier testling(JID("foo@tron\xc3\xa7on.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addDNSName("xn--tronon-zua.com");
-
- CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithMatchingDNSNameWithWildcard() {
- ServerIdentityVerifier testling(JID("foo@im.bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addDNSName("*.bar.com");
-
- CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithMatchingDNSNameWithWildcardMatchingNoComponents() {
- ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addDNSName("*.bar.com");
-
- CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithDNSNameWithWildcardMatchingTwoComponents() {
- ServerIdentityVerifier testling(JID("foo@xmpp.im.bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addDNSName("*.bar.com");
-
- CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithMatchingSRVNameWithoutService() {
- ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addSRVName("bar.com");
-
- CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithMatchingSRVNameWithService() {
- ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addSRVName("_xmpp-client.bar.com");
-
- CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithMatchingSRVNameWithServiceAndWildcard() {
- ServerIdentityVerifier testling(JID("foo@im.bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addSRVName("_xmpp-client.*.bar.com");
-
- CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithMatchingSRVNameWithDifferentService() {
- ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addSRVName("_xmpp-server.bar.com");
-
- CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
- }
-
- void testCertificateVerifies_WithMatchingXmppAddr() {
- ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addXMPPAddress("bar.com");
+ CPPUNIT_TEST_SUITE(ServerIdentityVerifierTest);
+ CPPUNIT_TEST(testCertificateVerifies_WithoutMatchingDNSName);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingDNSName);
+ CPPUNIT_TEST(testCertificateVerifies_WithSecondMatchingDNSName);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingInternationalDNSName);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingDNSNameWithWildcard);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingDNSNameWithWildcardMatchingNoComponents);
+ CPPUNIT_TEST(testCertificateVerifies_WithDNSNameWithWildcardMatchingTwoComponents);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithoutService);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithService);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithServiceAndWildcard);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithDifferentService);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingXmppAddr);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingXmppAddrWithWildcard);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingInternationalXmppAddr);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingCNWithoutSAN);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingCNWithSAN);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithServerExpected);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithClientUnexpected);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ idnConverter = std::shared_ptr<IDNConverter>(PlatformIDNConverter::create());
+ }
+
+ void testCertificateVerifies_WithoutMatchingDNSName() {
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addDNSName("foo.com");
+
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingDNSName() {
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addDNSName("bar.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingDNSNameMixedCase() {
+ ServerIdentityVerifier testling(JID("foo@baR.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addDNSName("Bar.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithSecondMatchingDNSName() {
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addDNSName("foo.com");
+ certificate->addDNSName("bar.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingInternationalDNSName() {
+ ServerIdentityVerifier testling(JID("foo@tron\xc3\xa7on.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addDNSName("xn--tronon-zua.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingDNSNameWithWildcard() {
+ ServerIdentityVerifier testling(JID("foo@im.bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addDNSName("*.bar.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingDNSNameWithWildcardMatchingNoComponents() {
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addDNSName("*.bar.com");
+
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithDNSNameWithWildcardMatchingTwoComponents() {
+ ServerIdentityVerifier testling(JID("foo@xmpp.im.bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addDNSName("*.bar.com");
+
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingSRVNameWithoutService() {
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addSRVName("bar.com");
+
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingSRVNameWithService() {
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addSRVName("_xmpp-client.bar.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingSRVNameWithServiceAndWildcard() {
+ ServerIdentityVerifier testling(JID("foo@im.bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addSRVName("_xmpp-client.*.bar.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingSRVNameWithDifferentService() {
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addSRVName("_xmpp-server.bar.com");
+
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingSRVNameWithServerExpected() {
+ // Server-mode test which gets cert with "xmpp-server" SRV name
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get(), true);
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addSRVName("_xmpp-server.bar.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingSRVNameWithClientUnexpected() {
+ // Server-mode test which gets cert with "xmpp-client" SRV name
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get(), true);
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addSRVName("_xmpp-client.bar.com");
+
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingXmppAddr() {
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addXMPPAddress("bar.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingXmppAddrMixedCase() {
+ ServerIdentityVerifier testling(JID("foo@baR.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addXMPPAddress("bAr.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingXmppAddrWithWildcard() {
+ ServerIdentityVerifier testling(JID("foo@im.bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addXMPPAddress("*.bar.com");
+
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingXmppAddrWithWildcardMixedCase() {
+ ServerIdentityVerifier testling(JID("foo@im.bAr.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addXMPPAddress("*.baR.com");
- CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
- }
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
- void testCertificateVerifies_WithMatchingXmppAddrWithWildcard() {
- ServerIdentityVerifier testling(JID("foo@im.bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addXMPPAddress("*.bar.com");
+ void testCertificateVerifies_WithMatchingInternationalXmppAddr() {
+ ServerIdentityVerifier testling(JID("foo@tron\xc3\xa7.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addXMPPAddress("tron\xc3\xa7.com");
- CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
- }
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
- void testCertificateVerifies_WithMatchingInternationalXmppAddr() {
- ServerIdentityVerifier testling(JID("foo@tron\xc3\xa7.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addXMPPAddress("tron\xc3\xa7.com");
+ void testCertificateVerifies_WithMatchingInternationalXmppAddrMixedCase() {
+ ServerIdentityVerifier testling(JID("foo@tRon\xc3\xa7.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addXMPPAddress("trOn\xc3\xa7.com");
- CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
- }
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
- void testCertificateVerifies_WithMatchingCNWithoutSAN() {
- ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addCommonName("bar.com");
+ void testCertificateVerifies_WithMatchingCNWithoutSAN() {
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addCommonName("bar.com");
- CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
- }
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
- void testCertificateVerifies_WithMatchingCNWithSAN() {
- ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
- SimpleCertificate::ref certificate(new SimpleCertificate());
- certificate->addSRVName("foo.com");
- certificate->addCommonName("bar.com");
+ void testCertificateVerifies_WithMatchingCNWithSAN() {
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addSRVName("foo.com");
+ certificate->addCommonName("bar.com");
- CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
- }
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
- boost::shared_ptr<IDNConverter> idnConverter;
+ std::shared_ptr<IDNConverter> idnConverter;
};
CPPUNIT_TEST_SUITE_REGISTRATION(ServerIdentityVerifierTest);