diff options
Diffstat (limited to 'Swiften')
-rw-r--r-- | Swiften/Client/CoreClient.cpp | 31 | ||||
-rw-r--r-- | Swiften/Client/CoreClient.h | 12 | ||||
-rw-r--r-- | Swiften/Session/SessionStream.cpp | 1 | ||||
-rw-r--r-- | Swiften/Session/SessionStream.h | 10 | ||||
-rw-r--r-- | Swiften/StreamStack/TLSLayer.cpp | 2 | ||||
-rw-r--r-- | Swiften/StreamStack/TLSLayer.h | 4 | ||||
-rw-r--r-- | Swiften/TLS/CAPICertificate.cpp | 151 | ||||
-rw-r--r-- | Swiften/TLS/CAPICertificate.h | 176 | ||||
-rw-r--r-- | Swiften/TLS/CertificateWithKey.h | 12 | ||||
-rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLContext.cpp | 11 | ||||
-rw-r--r-- | Swiften/TLS/OpenSSL/OpenSSLContext.h | 2 | ||||
-rw-r--r-- | Swiften/TLS/SConscript | 1 | ||||
-rw-r--r-- | Swiften/TLS/Schannel/SchannelContext.cpp | 27 | ||||
-rw-r--r-- | Swiften/TLS/Schannel/SchannelContext.h | 2 | ||||
-rw-r--r-- | Swiften/TLS/TLSContext.h | 4 |
15 files changed, 204 insertions, 242 deletions
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp index 36bfe35..e2a8e5a 100644 --- a/Swiften/Client/CoreClient.cpp +++ b/Swiften/Client/CoreClient.cpp @@ -94,135 +94,110 @@ void CoreClient::connect(const std::string& host) { networkFactories->getDomainNameResolver(), host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, options.boshHTTPConnectProxyAuthPassword)); sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1)); bindSessionToStream(); } } void CoreClient::bindSessionToStream() { session_ = ClientSession::create(jid_, sessionStream_); session_->setCertificateTrustChecker(certificateTrustChecker); session_->setUseStreamCompression(options.useStreamCompression); session_->setAllowPLAINOverNonTLS(options.allowPLAINWithoutTLS); switch(options.useTLS) { case ClientOptions::UseTLSWhenAvailable: session_->setUseTLS(ClientSession::UseTLSWhenAvailable); break; case ClientOptions::NeverUseTLS: session_->setUseTLS(ClientSession::NeverUseTLS); break; case ClientOptions::RequireTLS: session_->setUseTLS(ClientSession::RequireTLS); break; } session_->setUseAcks(options.useAcks); stanzaChannel_->setSession(session_); session_->onFinished.connect(boost::bind(&CoreClient::handleSessionFinished, this, _1)); session_->onNeedCredentials.connect(boost::bind(&CoreClient::handleNeedCredentials, this)); session_->start(); } -bool CoreClient::isCAPIURI() { -#ifdef HAVE_SCHANNEL - if (!boost::iequals(certificate_.substr(0, 10), "certstore:")) { - return false; - } - - return true; - -#else - return false; -#endif -} - /** * Only called for TCP sessions. BOSH is handled inside the BOSHSessionStream. */ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connection) { resetConnector(); if (!connection) { if (options.forgetPassword) { purgePassword(); } onDisconnected(disconnectRequested_ ? boost::optional<ClientError>() : boost::optional<ClientError>(ClientError::ConnectionError)); } else { assert(!connection_); connection_ = connection; assert(!sessionStream_); sessionStream_ = boost::make_shared<BasicSessionStream>(ClientStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory()); - if (!certificate_.empty()) { - CertificateWithKey* cert; - -#if defined(SWIFTEN_PLATFORM_WIN32) - if (isCAPIURI()) { - cert = new CAPICertificate(certificate_); - } else { - cert = new PKCS12Certificate(certificate_, password_); - } -#else - cert = new PKCS12Certificate(certificate_, password_); -#endif - - sessionStream_->setTLSCertificate(cert); + if (certificate_ && !certificate_->isNull()) { + sessionStream_->setTLSCertificate(certificate_); } sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1)); bindSessionToStream(); } } void CoreClient::disconnect() { // FIXME: We should be able to do without this boolean. We just have to make sure we can tell the difference between // connector finishing without a connection due to an error or because of a disconnect. disconnectRequested_ = true; if (session_ && !session_->isFinished()) { session_->finish(); } else if (connector_) { connector_->stop(); } } -void CoreClient::setCertificate(const std::string& certificate) { +void CoreClient::setCertificate(CertificateWithKey::ref certificate) { certificate_ = certificate; } void CoreClient::handleSessionFinished(boost::shared_ptr<Error> error) { if (options.forgetPassword) { purgePassword(); } resetSession(); boost::optional<ClientError> actualError; if (error) { ClientError clientError; if (boost::shared_ptr<ClientSession::Error> actualError = boost::dynamic_pointer_cast<ClientSession::Error>(error)) { switch(actualError->type) { case ClientSession::Error::AuthenticationFailedError: clientError = ClientError(ClientError::AuthenticationFailedError); break; case ClientSession::Error::CompressionFailedError: clientError = ClientError(ClientError::CompressionFailedError); break; case ClientSession::Error::ServerVerificationFailedError: clientError = ClientError(ClientError::ServerVerificationFailedError); break; case ClientSession::Error::NoSupportedAuthMechanismsError: clientError = ClientError(ClientError::NoSupportedAuthMechanismsError); break; case ClientSession::Error::UnexpectedElementError: clientError = ClientError(ClientError::UnexpectedElementError); break; case ClientSession::Error::ResourceBindError: clientError = ClientError(ClientError::ResourceBindError); break; case ClientSession::Error::SessionStartError: clientError = ClientError(ClientError::SessionStartError); break; diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h index 6712e03..1b875d2 100644 --- a/Swiften/Client/CoreClient.h +++ b/Swiften/Client/CoreClient.h @@ -1,91 +1,97 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <string> #include <boost/shared_ptr.hpp> #include <Swiften/Base/boost_bsignals.h> #include <Swiften/Entity/Entity.h> #include <Swiften/JID/JID.h> #include <Swiften/Client/ClientError.h> #include <Swiften/Client/ClientOptions.h> #include <Swiften/Base/SafeByteArray.h> +#include <Swiften/TLS/CertificateWithKey.h> namespace Swift { class ChainedConnector; class Message; class Presence; class Error; class IQRouter; class TLSContextFactory; class ConnectionFactory; class Connection; class TimerFactory; class ClientSession; class StanzaChannel; class Stanza; class SessionStream; class CertificateTrustChecker; class NetworkFactories; class ClientSessionStanzaChannel; /** * The central class for communicating with an XMPP server. * * This class is responsible for setting up the connection with the XMPP * server, authenticating, and initializing the session. * * This class can be used directly in your application, although the Client * subclass provides more functionality and interfaces, and is better suited * for most needs. */ class CoreClient : public Entity { public: /** * Constructs a client for the given JID with the given password. * The given eventLoop will be used to post events to. */ CoreClient(const JID& jid, const SafeByteArray& password, NetworkFactories* networkFactories); ~CoreClient(); - void setCertificate(const std::string& certificate); + /** + * Set a client certificate to use for strong authentication with the server. + * Ensure that it is of the correct type for the TLS engine in use. + * This means, largely, PKCS12Certificate for OpenSSL and CAPICertificate for CAPI. + */ + void setCertificate(CertificateWithKey::ref certificate); /** * Connects the client to the server. * * After the connection is established, the client will set * initialize the stream and authenticate. */ void connect(const ClientOptions& = ClientOptions()); /** * Disconnects the client from the server. */ void disconnect(); void connect(const std::string& host); /** * Sends a message. */ void sendMessage(boost::shared_ptr<Message>); /** * Sends a presence stanza. */ void sendPresence(boost::shared_ptr<Presence>); /** * Sends raw, unchecked data. */ void sendData(const std::string& data); /** * Returns the IQ router for this client. */ IQRouter* getIQRouter() const { @@ -195,40 +201,40 @@ namespace Swift { * Called before onConnected signal is emmitted. */ virtual void handleConnected() {}; bool isCAPIURI(); private: void handleConnectorFinished(boost::shared_ptr<Connection>); void handleStanzaChannelAvailableChanged(bool available); void handleSessionFinished(boost::shared_ptr<Error>); void handleNeedCredentials(); void handleDataRead(const SafeByteArray&); void handleDataWritten(const SafeByteArray&); void handlePresenceReceived(boost::shared_ptr<Presence>); void handleMessageReceived(boost::shared_ptr<Message>); void handleStanzaAcked(boost::shared_ptr<Stanza>); void purgePassword(); void bindSessionToStream(); void resetConnector(); void resetSession(); void forceReset(); private: JID jid_; SafeByteArray password_; NetworkFactories* networkFactories; ClientSessionStanzaChannel* stanzaChannel_; IQRouter* iqRouter_; ClientOptions options; boost::shared_ptr<ChainedConnector> connector_; std::vector<ConnectionFactory*> proxyConnectionFactories; boost::shared_ptr<Connection> connection_; boost::shared_ptr<SessionStream> sessionStream_; boost::shared_ptr<ClientSession> session_; - std::string certificate_; + CertificateWithKey::ref certificate_; bool disconnectRequested_; CertificateTrustChecker* certificateTrustChecker; }; } diff --git a/Swiften/Session/SessionStream.cpp b/Swiften/Session/SessionStream.cpp index 487ad8b..0d73b63 100644 --- a/Swiften/Session/SessionStream.cpp +++ b/Swiften/Session/SessionStream.cpp @@ -1,15 +1,14 @@ /* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include <Swiften/Session/SessionStream.h> namespace Swift { SessionStream::~SessionStream() { - delete certificate; } }; diff --git a/Swiften/Session/SessionStream.h b/Swiften/Session/SessionStream.h index 58015b3..2ff2a56 100644 --- a/Swiften/Session/SessionStream.h +++ b/Swiften/Session/SessionStream.h @@ -1,89 +1,89 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <Swiften/Base/boost_bsignals.h> #include <boost/shared_ptr.hpp> #include <boost/optional.hpp> #include <Swiften/Elements/ProtocolHeader.h> #include <Swiften/Elements/Element.h> #include <Swiften/Base/Error.h> #include <Swiften/Base/SafeByteArray.h> #include <Swiften/TLS/CertificateWithKey.h> #include <Swiften/TLS/Certificate.h> #include <Swiften/TLS/CertificateVerificationError.h> namespace Swift { class SessionStream { public: class Error : public Swift::Error { public: enum Type { ParseError, TLSError, InvalidTLSCertificateError, ConnectionReadError, ConnectionWriteError }; Error(Type type) : type(type) {} Type type; }; - SessionStream(): certificate(0) {} + SessionStream(): certificate() {} virtual ~SessionStream(); virtual void close() = 0; virtual bool isOpen() = 0; virtual void writeHeader(const ProtocolHeader& header) = 0; virtual void writeFooter() = 0; virtual void writeElement(boost::shared_ptr<Element>) = 0; virtual void writeData(const std::string& data) = 0; virtual bool supportsZLibCompression() = 0; virtual void addZLibCompression() = 0; virtual bool supportsTLSEncryption() = 0; virtual void addTLSEncryption() = 0; virtual bool isTLSEncrypted() = 0; virtual void setWhitespacePingEnabled(bool enabled) = 0; virtual void resetXMPPParser() = 0; - void setTLSCertificate(CertificateWithKey* cert) { + void setTLSCertificate(CertificateWithKey::ref cert) { certificate = cert; } virtual bool hasTLSCertificate() { return certificate && !certificate->isNull(); } virtual Certificate::ref getPeerCertificate() const = 0; virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const = 0; virtual ByteArray getTLSFinishMessage() const = 0; boost::signal<void (const ProtocolHeader&)> onStreamStartReceived; boost::signal<void (boost::shared_ptr<Element>)> onElementReceived; boost::signal<void (boost::shared_ptr<Error>)> onClosed; boost::signal<void ()> onTLSEncrypted; boost::signal<void (const SafeByteArray&)> onDataRead; boost::signal<void (const SafeByteArray&)> onDataWritten; protected: - CertificateWithKey * getTLSCertificate() const { + CertificateWithKey::ref getTLSCertificate() const { return certificate; } private: - CertificateWithKey * certificate; + CertificateWithKey::ref certificate; }; } diff --git a/Swiften/StreamStack/TLSLayer.cpp b/Swiften/StreamStack/TLSLayer.cpp index b7efbcb..6d416bc 100644 --- a/Swiften/StreamStack/TLSLayer.cpp +++ b/Swiften/StreamStack/TLSLayer.cpp @@ -5,48 +5,48 @@ */ #include <Swiften/StreamStack/TLSLayer.h> #include <boost/bind.hpp> #include <Swiften/TLS/TLSContextFactory.h> #include <Swiften/TLS/TLSContext.h> namespace Swift { TLSLayer::TLSLayer(TLSContextFactory* factory) { context = factory->createTLSContext(); context->onDataForNetwork.connect(boost::bind(&TLSLayer::writeDataToChildLayer, this, _1)); context->onDataForApplication.connect(boost::bind(&TLSLayer::writeDataToParentLayer, this, _1)); context->onConnected.connect(onConnected); context->onError.connect(onError); } TLSLayer::~TLSLayer() { delete context; } void TLSLayer::connect() { context->connect(); } void TLSLayer::writeData(const SafeByteArray& data) { context->handleDataFromApplication(data); } void TLSLayer::handleDataRead(const SafeByteArray& data) { context->handleDataFromNetwork(data); } -bool TLSLayer::setClientCertificate(CertificateWithKey * certificate) { +bool TLSLayer::setClientCertificate(CertificateWithKey::ref certificate) { return context->setClientCertificate(certificate); } Certificate::ref TLSLayer::getPeerCertificate() const { return context->getPeerCertificate(); } boost::shared_ptr<CertificateVerificationError> TLSLayer::getPeerCertificateVerificationError() const { return context->getPeerCertificateVerificationError(); } } diff --git a/Swiften/StreamStack/TLSLayer.h b/Swiften/StreamStack/TLSLayer.h index 6dc9135..5aab26a 100644 --- a/Swiften/StreamStack/TLSLayer.h +++ b/Swiften/StreamStack/TLSLayer.h @@ -1,44 +1,44 @@ /* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include <Swiften/Base/boost_bsignals.h> #include <Swiften/Base/SafeByteArray.h> #include <Swiften/StreamStack/StreamLayer.h> #include <Swiften/TLS/Certificate.h> +#include <Swiften/TLS/CertificateWithKey.h> #include <Swiften/TLS/CertificateVerificationError.h> namespace Swift { class TLSContext; class TLSContextFactory; - class CertificateWithKey; class TLSLayer : public StreamLayer { public: TLSLayer(TLSContextFactory*); ~TLSLayer(); void connect(); - bool setClientCertificate(CertificateWithKey * cert); + bool setClientCertificate(CertificateWithKey::ref cert); Certificate::ref getPeerCertificate() const; boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; void writeData(const SafeByteArray& data); void handleDataRead(const SafeByteArray& data); TLSContext* getContext() const { return context; } public: boost::signal<void ()> onError; boost::signal<void ()> onConnected; private: TLSContext* context; }; } diff --git a/Swiften/TLS/CAPICertificate.cpp b/Swiften/TLS/CAPICertificate.cpp new file mode 100644 index 0000000..9a3bbc8 --- /dev/null +++ b/Swiften/TLS/CAPICertificate.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2012 Isode Limited, London, England. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +#pragma once + +#include <Swiften/TLS/CAPICertificate.h> + +#include <boost/algorithm/string/predicate.hpp> + + +namespace Swift { +CAPICertificate::CAPICertificate(const std::string& capiUri) + : valid_(false), uri_(capiUri), certStoreHandle_(0), certStore_(), certName_() { + setUri(capiUri); +} + +CAPICertificate::~CAPICertificate() { + if (certStoreHandle_) { + CertCloseStore(certStoreHandle_, 0); + } +} + +bool CAPICertificate::isNull() const { + return uri_.empty() || !valid_; +} + +const std::string& CAPICertificate::getCertStoreName() const { + return certStore_; +} + +const std::string& CAPICertificate::getCertName() const { + return certName_; +} + +void CAPICertificate::setUri (const std::string& capiUri) { + + valid_ = false; + + /* Syntax: "certstore:" [<cert_store> ":"] <cert_id> */ + + if (!boost::iequals(capiUri.substr(0, 10), "certstore:")) { + return; + } + + /* Substring of subject: uses "storename" */ + std::string capi_identity = capiUri.substr(10); + std::string new_certStore_name; + size_t pos = capi_identity.find_first_of (':'); + + if (pos == std::string::npos) { + /* Using the default certificate store */ + new_certStore_name = "MY"; + certName_ = capi_identity; + } + else { + new_certStore_name = capi_identity.substr(0, pos); + certName_ = capi_identity.substr(pos + 1); + } + + PCCERT_CONTEXT pCertContext = NULL; + + if (certStoreHandle_ != NULL) { + if (new_certStore_name != certStore_) { + CertCloseStore(certStoreHandle_, 0); + certStoreHandle_ = NULL; + } + } + + if (certStoreHandle_ == NULL) { + certStoreHandle_ = CertOpenSystemStore(0, certStore_.c_str()); + if (!certStoreHandle_) { + return; + } + } + + certStore_ = new_certStore_name; + + /* NB: This might have to change, depending on how we locate certificates */ + + // Find client certificate. Note that this sample just searches for a + // certificate that contains the user name somewhere in the subject name. + pCertContext = CertFindCertificateInStore(certStoreHandle_, + X509_ASN_ENCODING, + 0, // dwFindFlags + CERT_FIND_SUBJECT_STR_A, + certName_.c_str(), // *pvFindPara + NULL ); // pPrevCertContext + + if (!pCertContext) { + 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(pCertContext, + CERT_KEY_PROV_INFO_PROP_ID, + NULL, + &len)) { + CertFreeCertificateContext(pCertContext); + return; + } + + pinfo = static_cast<CRYPT_KEY_PROV_INFO *>(malloc(len)); + if (!pinfo) { + CertFreeCertificateContext(pCertContext); + return; + } + + if (!CertGetCertificateContextProperty(pCertContext, + CERT_KEY_PROV_INFO_PROP_ID, + pinfo, + &len)) { + CertFreeCertificateContext(pCertContext); + free(pinfo); + return; + } + + CertFreeCertificateContext(pCertContext); + + // Now verify if we have access to the private key + if (!CryptAcquireContextW(&hprov, + pinfo->pwszContainerName, + pinfo->pwszProvName, + pinfo->dwProvType, + 0)) { + free(pinfo); + return; + } + + if (!CryptGetUserKey(hprov, pinfo->dwKeySpec, &key)) { + CryptReleaseContext(hprov, 0); + free(pinfo); + return; + } + + CryptDestroyKey(key); + CryptReleaseContext(hprov, 0); + free(pinfo); + + valid_ = true; +} + +} diff --git a/Swiften/TLS/CAPICertificate.h b/Swiften/TLS/CAPICertificate.h index fcdb4c2..d9e2704 100644 --- a/Swiften/TLS/CAPICertificate.h +++ b/Swiften/TLS/CAPICertificate.h @@ -1,196 +1,42 @@ /* * Copyright (c) 2012 Isode Limited, London, England. * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include <Swiften/Base/SafeByteArray.h> #include <Swiften/TLS/CertificateWithKey.h> -#include <boost/algorithm/string/predicate.hpp> - #define SECURITY_WIN32 +#include <Windows.h> #include <WinCrypt.h> namespace Swift { class CAPICertificate : public Swift::CertificateWithKey { public: - CAPICertificate(const std::string& capiUri) - : valid_(false), uri_(capiUri), cert_store_handle_(0), cert_store_(NULL), cert_name_(NULL) { - setUri(capiUri); - } - - virtual ~CAPICertificate() { - if (cert_store_handle_ != NULL) - { - CertCloseStore(cert_store_handle_, 0); - } - } - - virtual bool isNull() const { - return uri_.empty() || !valid_; - } - - virtual bool isPrivateKeyExportable() const { - /* We can check with CAPI, but for now the answer is "no" */ - return false; - } - - virtual const std::string& getCertStoreName() const { - return cert_store_; - } - - virtual const std::string& getCertName() const { - return cert_name_; - } - - const ByteArray& getData() const { -////Might need to throw an exception here, or really generate PKCS12 blob from CAPI data? - assert(0); - } - - void setData(const ByteArray& data) { - assert(0); - } - - const SafeByteArray& getPassword() const { -/////Can't pass NULL to createSafeByteArray! -/////Should this throw an exception instead? - return createSafeByteArray(""); - } - - protected: - void setUri (const std::string& capiUri) { - - valid_ = false; - - /* Syntax: "certstore:" [<cert_store> ":"] <cert_id> */ - - if (!boost::iequals(capiUri.substr(0, 10), "certstore:")) { - return; - } - - /* Substring of subject: uses "storename" */ - std::string capi_identity = capiUri.substr(10); - std::string new_cert_store_name; - size_t pos = capi_identity.find_first_of (':'); - - if (pos == std::string::npos) { - /* Using the default certificate store */ - new_cert_store_name = "MY"; - cert_name_ = capi_identity; - } else { - new_cert_store_name = capi_identity.substr(0, pos); - cert_name_ = capi_identity.substr(pos + 1); - } + CAPICertificate(const std::string& capiUri); - PCCERT_CONTEXT pCertContext = NULL; + virtual ~CAPICertificate(); - if (cert_store_handle_ != NULL) - { - if (new_cert_store_name != cert_store_) { - CertCloseStore(cert_store_handle_, 0); - cert_store_handle_ = NULL; - } - } + virtual bool isNull() const; - if (cert_store_handle_ == NULL) - { - cert_store_handle_ = CertOpenSystemStore(0, cert_store_.c_str()); - if (!cert_store_handle_) - { - return; - } - } + const std::string& getCertStoreName() const; - cert_store_ = new_cert_store_name; + const std::string& getCertName() const; - /* NB: This might have to change, depending on how we locate certificates */ - - // Find client certificate. Note that this sample just searches for a - // certificate that contains the user name somewhere in the subject name. - pCertContext = CertFindCertificateInStore(cert_store_handle_, - X509_ASN_ENCODING, - 0, // dwFindFlags - CERT_FIND_SUBJECT_STR_A, - cert_name_.c_str(), // *pvFindPara - NULL ); // pPrevCertContext - - if (pCertContext == NULL) - { - 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(pCertContext, - CERT_KEY_PROV_INFO_PROP_ID, - NULL, - &len)) - { - CertFreeCertificateContext(pCertContext); - return; - } - - pinfo = static_cast<CRYPT_KEY_PROV_INFO *>(malloc(len)); - if (!pinfo) { - CertFreeCertificateContext(pCertContext); - return; - } - - if (!CertGetCertificateContextProperty(pCertContext, - CERT_KEY_PROV_INFO_PROP_ID, - pinfo, - &len)) - { - CertFreeCertificateContext(pCertContext); - free(pinfo); - return; - } - - CertFreeCertificateContext(pCertContext); - - // Now verify if we have access to the private key - if (!CryptAcquireContextW(&hprov, - pinfo->pwszContainerName, - pinfo->pwszProvName, - pinfo->dwProvType, - 0)) - { - free(pinfo); - return; - } - - if (!CryptGetUserKey(hprov, pinfo->dwKeySpec, &key)) - { - CryptReleaseContext(hprov, 0); - free(pinfo); - return; - } - - CryptDestroyKey(key); - CryptReleaseContext(hprov, 0); - free(pinfo); - - valid_ = true; - } + private: + void setUri (const std::string& capiUri); private: bool valid_; std::string uri_; - HCERTSTORE cert_store_handle_; + HCERTSTORE certStoreHandle_; /* Parsed components of the uri_ */ - std::string cert_store_; - std::string cert_name_; + std::string certStore_; + std::string certName_; }; } diff --git a/Swiften/TLS/CertificateWithKey.h b/Swiften/TLS/CertificateWithKey.h index 6f6ea39..1bdeb03 100644 --- a/Swiften/TLS/CertificateWithKey.h +++ b/Swiften/TLS/CertificateWithKey.h @@ -1,32 +1,22 @@ /* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <Swiften/Base/SafeByteArray.h> namespace Swift { class CertificateWithKey { public: + typedef boost::shared_ptr<CertificateWithKey> ref; CertificateWithKey() {} virtual ~CertificateWithKey() {} virtual bool isNull() const = 0; - virtual bool isPrivateKeyExportable() const = 0; - - virtual const std::string& getCertStoreName() const = 0; - - virtual const std::string& getCertName() const = 0; - - virtual const ByteArray& getData() const = 0; - - virtual void setData(const ByteArray& data) = 0; - - virtual const SafeByteArray& getPassword() const = 0; }; } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp index dd3462f..8076967 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp @@ -154,92 +154,89 @@ void OpenSSLContext::handleDataFromNetwork(const SafeByteArray& data) { case Connected: sendPendingDataToApplication(); break; case Start: assert(false); break; case Error: /*assert(false);*/ break; } } void OpenSSLContext::handleDataFromApplication(const SafeByteArray& data) { if (SSL_write(handle_, vecptr(data), data.size()) >= 0) { sendPendingDataToNetwork(); } else { state_ = Error; onError(); } } 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(); } } bool OpenSSLContext::setClientCertificate(CertificateWithKey * certificate) { - if (!certificate || certificate->isNull()) { - return false; - } - - if (!certificate->isPrivateKeyExportable()) { + 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(certificate->getData()), certificate->getData().size()); + BIO_write(bio, vecptr(certificate->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(certificate->getPassword())), &privateKeyPtr, &certPtr, &caCertsPtr); + 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; } Certificate::ref OpenSSLContext::getPeerCertificate() const { boost::shared_ptr<X509> x509Cert(SSL_get_peer_certificate(handle_), X509_free); if (x509Cert) { return boost::make_shared<OpenSSLCertificate>(x509Cert); } else { return Certificate::ref(); } } 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)); } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h index b53e715..e98fb49 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.h +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h @@ -1,53 +1,53 @@ /* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <openssl/ssl.h> #include <Swiften/Base/boost_bsignals.h> #include <boost/noncopyable.hpp> #include <Swiften/TLS/TLSContext.h> #include <Swiften/Base/ByteArray.h> namespace Swift { class CertificateWithKey; class OpenSSLContext : public TLSContext, boost::noncopyable { public: OpenSSLContext(); ~OpenSSLContext(); void connect(); - bool setClientCertificate(CertificateWithKey * cert); + bool setClientCertificate(CertificateWithKey::ref cert); void handleDataFromNetwork(const SafeByteArray&); void handleDataFromApplication(const SafeByteArray&); Certificate::ref getPeerCertificate() const; boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; virtual ByteArray getFinishMessage() const; private: static void ensureLibraryInitialized(); static CertificateVerificationError::Type getVerificationErrorTypeForResult(int); void doConnect(); void sendPendingDataToNetwork(); void sendPendingDataToApplication(); private: enum State { Start, Connecting, Connected, Error }; State state_; SSL_CTX* context_; SSL* handle_; BIO* readBIO_; BIO* writeBIO_; }; } diff --git a/Swiften/TLS/SConscript b/Swiften/TLS/SConscript index a71a446..0e95b8b 100644 --- a/Swiften/TLS/SConscript +++ b/Swiften/TLS/SConscript @@ -1,33 +1,34 @@ Import("swiften_env") objects = swiften_env.SwiftenObject([ "Certificate.cpp", "CertificateFactory.cpp", "CertificateTrustChecker.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") elif myenv.get("HAVE_SCHANNEL", 0) : objects += myenv.StaticObject([ + "CAPICertificate.cpp", "Schannel/SchannelContext.cpp", "Schannel/SchannelCertificate.cpp", "Schannel/SchannelContextFactory.cpp", ]) myenv.Append(CPPDEFINES = "HAVE_SCHANNEL") objects += myenv.SwiftenObject(["PlatformTLSFactories.cpp"]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/TLS/Schannel/SchannelContext.cpp b/Swiften/TLS/Schannel/SchannelContext.cpp index 6f50b3a..ddbebcb 100644 --- a/Swiften/TLS/Schannel/SchannelContext.cpp +++ b/Swiften/TLS/Schannel/SchannelContext.cpp @@ -1,55 +1,56 @@ /* * Copyright (c) 2011 Soren Dreijer * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ -#include "Swiften/TLS/Schannel/SchannelContext.h" -#include "Swiften/TLS/Schannel/SchannelCertificate.h" +#include <Swiften/TLS/Schannel/SchannelContext.h> +#include <Swiften/TLS/Schannel/SchannelCertificate.h> +#include <Swiften/TLS/CAPICertificate.h> namespace Swift { //------------------------------------------------------------------------ SchannelContext::SchannelContext() : m_state(Start) , m_secContext(0) , m_verificationError(CertificateVerificationError::UnknownError) , m_my_cert_store(NULL) , m_cert_store_name("MY") -, m_cert_name(NULL) +, m_cert_name() { 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() { if (m_my_cert_store) CertCloseStore(m_my_cert_store, 0); } //------------------------------------------------------------------------ void SchannelContext::determineStreamSizes() { QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_STREAM_SIZES, &m_streamSizes); } //------------------------------------------------------------------------ void SchannelContext::connect() { PCCERT_CONTEXT pCertContext = NULL; m_state = Connecting; @@ -485,86 +486,82 @@ void SchannelContext::encryptAndSendData(const SafeByteArray& data) 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(); return; } sendDataOnNetwork(&sendBuffer[0], outBuffers[0].cbBuffer + outBuffers[1].cbBuffer + outBuffers[2].cbBuffer); bytesSent += bytesToSend; } while (bytesSent < data.size()); } //------------------------------------------------------------------------ -bool SchannelContext::setClientCertificate(CertificateWithKey * certificate) +bool SchannelContext::setClientCertificate(CertificateWithKey::ref certificate) { - if (!certificate || certificate->isNull()) { + boost::shared_ptr<CAPICertificate> capiCertificate = boost::dynamic_pointer_cast<CAPICertificate>(certificate); + if (!capiCertificate || capiCertificate->isNull()) { return false; } - if (!certificate->isPrivateKeyExportable()) { - // We assume that the Certificate Store Name/Certificate Name - // are valid at this point - m_cert_store_name = certificate->getCertStoreName(); - m_cert_name = certificate->getCertName(); - - return true; - } - - return false; + // 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(); + return true; } //------------------------------------------------------------------------ Certificate::ref SchannelContext::getPeerCertificate() const { SchannelCertificate::ref pCertificate; ScopedCertContext pServerCert; SECURITY_STATUS status = QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset()); if (status != SEC_E_OK) return pCertificate; pCertificate.reset( new SchannelCertificate(pServerCert) ); return pCertificate; } //------------------------------------------------------------------------ CertificateVerificationError::ref SchannelContext::getPeerCertificateVerificationError() const { boost::shared_ptr<CertificateVerificationError> pCertError; if (m_state == Error) pCertError.reset( new CertificateVerificationError(m_verificationError) ); return pCertError; } //------------------------------------------------------------------------ ByteArray SchannelContext::getFinishMessage() const { // TODO: Implement diff --git a/Swiften/TLS/Schannel/SchannelContext.h b/Swiften/TLS/Schannel/SchannelContext.h index 0cdb3d7..7726c41 100644 --- a/Swiften/TLS/Schannel/SchannelContext.h +++ b/Swiften/TLS/Schannel/SchannelContext.h @@ -5,71 +5,71 @@ */ #pragma once #include "Swiften/Base/boost_bsignals.h" #include "Swiften/TLS/TLSContext.h" #include "Swiften/TLS/Schannel/SchannelUtil.h" #include <Swiften/TLS/CertificateWithKey.h> #include "Swiften/Base/ByteArray.h" #define SECURITY_WIN32 #include <Windows.h> #include <Schannel.h> #include <security.h> #include <schnlsp.h> #include <boost/noncopyable.hpp> namespace Swift { class SchannelContext : public TLSContext, boost::noncopyable { public: typedef boost::shared_ptr<SchannelContext> sp_t; public: SchannelContext(); ~SchannelContext(); // // TLSContext // virtual void connect(); - virtual bool setClientCertificate(CertificateWithKey * cert); + virtual bool setClientCertificate(CertificateWithKey::ref cert); virtual void handleDataFromNetwork(const SafeByteArray& data); virtual void handleDataFromApplication(const SafeByteArray& data); virtual Certificate::ref getPeerCertificate() const; virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const; virtual ByteArray getFinishMessage() const; private: void determineStreamSizes(); void continueHandshake(const SafeByteArray& data); void indicateError(); void sendDataOnNetwork(const void* pData, size_t dataSize); void forwardDataToApplication(const void* pData, size_t dataSize); void decryptAndProcessData(const SafeByteArray& data); void encryptAndSendData(const SafeByteArray& data); void appendNewData(const SafeByteArray& data); private: enum SchannelState { Start, Connecting, Connected, Error }; SchannelState m_state; CertificateVerificationError m_verificationError; diff --git a/Swiften/TLS/TLSContext.h b/Swiften/TLS/TLSContext.h index ada813a..9dee902 100644 --- a/Swiften/TLS/TLSContext.h +++ b/Swiften/TLS/TLSContext.h @@ -1,41 +1,41 @@ /* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <Swiften/Base/boost_bsignals.h> #include <boost/shared_ptr.hpp> #include <Swiften/Base/SafeByteArray.h> #include <Swiften/TLS/Certificate.h> +#include <Swiften/TLS/CertificateWithKey.h> #include <Swiften/TLS/CertificateVerificationError.h> namespace Swift { - class CertificateWithKey; class TLSContext { public: virtual ~TLSContext(); virtual void connect() = 0; - virtual bool setClientCertificate(CertificateWithKey * cert) = 0; + virtual bool setClientCertificate(CertificateWithKey::ref cert) = 0; virtual void handleDataFromNetwork(const SafeByteArray&) = 0; virtual void handleDataFromApplication(const SafeByteArray&) = 0; virtual Certificate::ref getPeerCertificate() const = 0; virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const = 0; virtual ByteArray getFinishMessage() const = 0; public: boost::signal<void (const SafeByteArray&)> onDataForNetwork; boost::signal<void (const SafeByteArray&)> onDataForApplication; boost::signal<void ()> onError; boost::signal<void ()> onConnected; }; } |