diff options
Diffstat (limited to 'Swiften/TLS')
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); |