From fa705718be1f98185557a09cf155ed66cbc740e2 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 22 Feb 2012 13:31:39 +0000 Subject: Fix up for previous CAPI patch Now connects successfully with or without TLS(with cert) diff --git a/BuildTools/SCons/SConscript.boot b/BuildTools/SCons/SConscript.boot index 2ea05db..188184c 100644 --- a/BuildTools/SCons/SConscript.boot +++ b/BuildTools/SCons/SConscript.boot @@ -134,6 +134,7 @@ if env["debug"] : if env["PLATFORM"] == "win32" : env.Append(CCFLAGS = ["/Zi", "/MDd"]) env.Append(LINKFLAGS = ["/DEBUG"]) + env.Append(CPPDEFINES = ["_ITERATOR_DEBUG_LEVEL=0"]) else : env.Append(CCFLAGS = ["-g"]) elif env["PLATFORM"] == "win32" : diff --git a/COPYING b/COPYING index 6bcfda6..15d92fa 100644 --- a/COPYING +++ b/COPYING @@ -839,3 +839,17 @@ Redistributions in binary form must reproduce the above copyright notice, this l Neither the name of their organizations nor the names of the contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --- END OF BSD LICENSE + +--- START OF SIMPLIFIED BSD LICENSE + +Copyright (c) 2012 Isode Limited, London, England. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WA RRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- END OF SIMPLIFIED BSD LICENSE diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 4f869a7..d60bcbb 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -159,7 +159,7 @@ MainController::MainController( } - loginWindow_->onLoginRequest.connect(boost::bind(&MainController::handleLoginRequest, this, _1, _2, _3, _4, _5)); + loginWindow_->onLoginRequest.connect(boost::bind(&MainController::handleLoginRequest, this, _1, _2, _3, _4, _5, _6)); loginWindow_->onPurgeSavedLoginRequest.connect(boost::bind(&MainController::handlePurgeSavedLoginRequest, this, _1)); loginWindow_->onCancelLoginRequest.connect(boost::bind(&MainController::handleCancelLoginRequest, this)); loginWindow_->onQuitRequest.connect(boost::bind(&MainController::handleQuitRequest, this)); @@ -175,7 +175,8 @@ MainController::MainController( if (loginAutomatically) { profileSettings_ = new ProfileSettingsProvider(selectedLoginJID, settings_); - handleLoginRequest(selectedLoginJID, cachedPassword, cachedCertificate, true, true); + /* FIXME: deal with autologin with a cert*/ + handleLoginRequest(selectedLoginJID, cachedPassword, cachedCertificate, CertificateWithKey::ref(), true, true); } else { profileSettings_ = NULL; } @@ -416,7 +417,7 @@ void MainController::handleInputIdleChanged(bool idle) { } } -void MainController::handleLoginRequest(const std::string &username, const std::string &password, const std::string& certificateFile, bool remember, bool loginAutomatically) { +void MainController::handleLoginRequest(const std::string &username, const std::string &password, const std::string& certificatePath, CertificateWithKey::ref certificate, bool remember, bool loginAutomatically) { jid_ = JID(username); if (!jid_.isValid() || jid_.getNode().empty()) { loginWindow_->setMessage(QT_TRANSLATE_NOOP("", "User address invalid. User address should be of the form 'alice@wonderland.lit'")); @@ -427,7 +428,7 @@ void MainController::handleLoginRequest(const std::string &username, const std:: profileSettings_ = new ProfileSettingsProvider(username, settings_); if (!settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { profileSettings_->storeString("jid", username); - profileSettings_->storeString("certificate", certificateFile); + profileSettings_->storeString("certificate", certificatePath); profileSettings_->storeString("pass", (remember || loginAutomatically) ? password : ""); settings_->storeSetting(SettingConstants::LAST_LOGIN_JID, username); settings_->storeSetting(SettingConstants::LOGIN_AUTOMATICALLY, loginAutomatically); @@ -435,7 +436,7 @@ void MainController::handleLoginRequest(const std::string &username, const std:: } password_ = password; - certificateFile_ = certificateFile; + certificate_ = certificate; performLoginFromCachedCredentials(); } } @@ -480,8 +481,8 @@ void MainController::performLoginFromCachedCredentials() { presenceNotifier_->onNotificationActivated.connect(boost::bind(&MainController::handleNotificationClicked, this, _1)); eventNotifier_ = new EventNotifier(eventController_, notifier_, client_->getAvatarManager(), client_->getNickResolver()); eventNotifier_->onNotificationActivated.connect(boost::bind(&MainController::handleNotificationClicked, this, _1)); - if (!certificateFile_.empty()) { - client_->setCertificate(certificateFile_); + if (certificate_ && !certificate_->isNull()) { + client_->setCertificate(certificate_); } boost::shared_ptr presence(new Presence()); presence->setShow(static_cast(profileSettings_->getIntSetting("lastShow", StatusShow::Online))); diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index 45e4ccf..14de4eb 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -91,7 +91,7 @@ namespace Swift { private: void resetClient(); void handleConnected(); - void handleLoginRequest(const std::string& username, const std::string& password, const std::string& certificateFile, bool remember, bool loginAutomatically); + void handleLoginRequest(const std::string& username, const std::string& password, const std::string& certificatePath, CertificateWithKey::ref certificate, bool remember, bool loginAutomatically); void handleCancelLoginRequest(); void handleQuitRequest(); void handleChangeStatusRequest(StatusShow::Type show, const std::string &statusText); @@ -153,7 +153,7 @@ namespace Swift { XMPPURIController* xmppURIController_; std::string vCardPhotoHash_; std::string password_; - std::string certificateFile_; + CertificateWithKey::ref certificate_; boost::shared_ptr lastDisconnectError_; bool useDelayForLatency_; UserSearchController* userSearchControllerChat_; diff --git a/Swift/Controllers/UIInterfaces/LoginWindow.h b/Swift/Controllers/UIInterfaces/LoginWindow.h index a8ee5a4..bbbbe35 100644 --- a/Swift/Controllers/UIInterfaces/LoginWindow.h +++ b/Swift/Controllers/UIInterfaces/LoginWindow.h @@ -1,16 +1,17 @@ /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2012 Kevin Smith * 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 #include #include #include +#include namespace Swift { class MainWindow; @@ -25,7 +26,8 @@ namespace Swift { virtual void setIsLoggingIn(bool loggingIn) = 0; virtual void addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate) = 0; virtual void removeAvailableAccount(const std::string& jid) = 0; - boost::signal onLoginRequest; + /** The certificate is what is used for login, the certificatePath is used for remembering paths to populate the loginwindow with*/ + boost::signal onLoginRequest; virtual void setLoginAutomatically(bool loginAutomatically) = 0; virtual void quit() = 0; /** Blocking request whether a cert should be permanently trusted.*/ diff --git a/Swift/QtUI/CAPICertificateSelector.cpp b/Swift/QtUI/CAPICertificateSelector.cpp index 44f5793..aa41d70 100644 --- a/Swift/QtUI/CAPICertificateSelector.cpp +++ b/Swift/QtUI/CAPICertificateSelector.cpp @@ -6,12 +6,14 @@ #include +#include "CAPICertificateSelector.h" + #define SECURITY_WIN32 #include #include #include -#include "CAPICertificateSelector.h" +#include namespace Swift { @@ -134,5 +136,8 @@ std::string selectCAPICertificate() { } } +bool isCAPIURI(std::string uri) { + return (boost::iequals(uri.substr(0, 10), "certstore:")); +} } diff --git a/Swift/QtUI/CAPICertificateSelector.h b/Swift/QtUI/CAPICertificateSelector.h index 9a0ee92..714f1c5 100644 --- a/Swift/QtUI/CAPICertificateSelector.h +++ b/Swift/QtUI/CAPICertificateSelector.h @@ -10,4 +10,5 @@ namespace Swift { std::string selectCAPICertificate(); + bool isCAPIURI(std::string uri); } diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp index 6b9d389..a3b7837 100644 --- a/Swift/QtUI/QtLoginWindow.cpp +++ b/Swift/QtUI/QtLoginWindow.cpp @@ -43,7 +43,9 @@ #ifdef HAVE_SCHANNEL #include "CAPICertificateSelector.h" +#include #endif +#include namespace Swift{ @@ -345,7 +347,19 @@ void QtLoginWindow::loginClicked() { return; } } - onLoginRequest(Q2PSTRING(username_->currentText()), Q2PSTRING(password_->text()), Q2PSTRING(certificateFile_), remember_->isChecked(), loginAutomatically_->isChecked()); + CertificateWithKey::ref certificate; + std::string certificateString = Q2PSTRING(certificateFile_); +#if defined(HAVE_SCHANNEL) + if (isCAPIURI(certificateString)) { + certificate = boost::make_shared(certificateString); + } else { + certificate = boost::make_shared(certificateString, createSafeByteArray(Q2PSTRING(password_->text()))); + } +#else + certificate = boost::make_shared(certificateString, createSafeByteArray(Q2PSTRING(password_->text()))); +#endif + + onLoginRequest(Q2PSTRING(username_->currentText()), Q2PSTRING(password_->text()), certificateString, certificate, remember_->isChecked(), loginAutomatically_->isChecked()); if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { /* Mustn't remember logins */ username_->clearEditText(); password_->setText(""); @@ -362,7 +376,7 @@ void QtLoginWindow::setLoginAutomatically(bool loginAutomatically) { void QtLoginWindow::handleCertficateChecked(bool checked) { if (checked) { #ifdef HAVE_SCHANNEL - certificateFile_ = selectCAPICertificate(); + certificateFile_ = P2QSTRING(selectCAPICertificate()); if (certificateFile_.isEmpty()) { certificateButton_->setChecked(false); } diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index a8b8c78..95c4305 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -57,6 +57,9 @@ if env["PLATFORM"] == "win32" : myenv.Append(LIBS = "qtmain") if myenv.get("HAVE_SCHANNEL", 0) : myenv.Append(LIBS = "Cryptui") + myenv.Append(CPPDEFINES = "HAVE_SCHANNEL") + if env["debug"] : + myenv.Append(LINKFLAGS = ["/NODEFAULTLIB:msvcrt"]) myenv.WriteVal("DefaultTheme.qrc", myenv.Value(generateDefaultTheme(myenv.Dir("#/Swift/resources/themes/Default")))) 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 @@ -126,19 +126,6 @@ void CoreClient::bindSessionToStream() { 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. */ @@ -156,20 +143,8 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr connectio assert(!sessionStream_); sessionStream_ = boost::make_shared(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)); @@ -190,7 +165,7 @@ void CoreClient::disconnect() { } } -void CoreClient::setCertificate(const std::string& certificate) { +void CoreClient::setCertificate(CertificateWithKey::ref certificate) { certificate_ = certificate; } 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,5 +1,5 @@ /* - * 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. */ @@ -15,6 +15,7 @@ #include #include #include +#include namespace Swift { class ChainedConnector; @@ -53,7 +54,12 @@ namespace Swift { 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. @@ -227,7 +233,7 @@ namespace Swift { boost::shared_ptr connection_; boost::shared_ptr sessionStream_; boost::shared_ptr 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 @@ -9,7 +9,6 @@ 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,5 +1,5 @@ /* - * 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. */ @@ -36,7 +36,7 @@ namespace Swift { Type type; }; - SessionStream(): certificate(0) {} + SessionStream(): certificate() {} virtual ~SessionStream(); @@ -58,7 +58,7 @@ namespace Swift { virtual void resetXMPPParser() = 0; - void setTLSCertificate(CertificateWithKey* cert) { + void setTLSCertificate(CertificateWithKey::ref cert) { certificate = cert; } @@ -79,11 +79,11 @@ namespace Swift { boost::signal 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 @@ -37,7 +37,7 @@ void TLSLayer::handleDataRead(const SafeByteArray& data) { context->handleDataFromNetwork(data); } -bool TLSLayer::setClientCertificate(CertificateWithKey * certificate) { +bool TLSLayer::setClientCertificate(CertificateWithKey::ref certificate) { return context->setClientCertificate(certificate); } 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 @@ -9,12 +9,12 @@ #include #include #include +#include #include namespace Swift { class TLSContext; class TLSContextFactory; - class CertificateWithKey; class TLSLayer : public StreamLayer { public: @@ -22,7 +22,7 @@ namespace Swift { ~TLSLayer(); void connect(); - bool setClientCertificate(CertificateWithKey * cert); + bool setClientCertificate(CertificateWithKey::ref cert); Certificate::ref getPeerCertificate() const; boost::shared_ptr getPeerCertificateVerificationError() const; 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 + +#include + + +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:" [ ":"] */ + + 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(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 @@ -9,188 +9,34 @@ #include #include -#include - #define SECURITY_WIN32 +#include #include 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:" [ ":"] */ - - 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(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 @@ -11,22 +11,12 @@ namespace Swift { class CertificateWithKey { public: + typedef boost::shared_ptr 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 @@ -186,17 +186,14 @@ void OpenSSLContext::sendPendingDataToApplication() { } bool OpenSSLContext::setClientCertificate(CertificateWithKey * certificate) { - if (!certificate || certificate->isNull()) { - return false; - } - - if (!certificate->isPrivateKeyExportable()) { + boost::shared_ptr pkcs12Certificate = boost::dynamic_pointer_cast(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(d2i_PKCS12_bio(bio, NULL), PKCS12_free); BIO_free(bio); if (!pkcs12) { @@ -207,7 +204,7 @@ bool OpenSSLContext::setClientCertificate(CertificateWithKey * certificate) { X509 *certPtr = 0; EVP_PKEY* privateKeyPtr = 0; STACK_OF(X509)* caCertsPtr = 0; - int result = PKCS12_parse(pkcs12.get(), reinterpret_cast(vecptr(certificate->getPassword())), &privateKeyPtr, &certPtr, &caCertsPtr); + int result = PKCS12_parse(pkcs12.get(), reinterpret_cast(vecptr(pkcs12Certificate->getPassword())), &privateKeyPtr, &certPtr, &caCertsPtr); if (result != 1) { return false; } 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 @@ -22,7 +22,7 @@ namespace Swift { ~OpenSSLContext(); void connect(); - bool setClientCertificate(CertificateWithKey * cert); + bool setClientCertificate(CertificateWithKey::ref cert); void handleDataFromNetwork(const SafeByteArray&); void handleDataFromApplication(const SafeByteArray&); diff --git a/Swiften/TLS/SConscript b/Swiften/TLS/SConscript index a71a446..0e95b8b 100644 --- a/Swiften/TLS/SConscript +++ b/Swiften/TLS/SConscript @@ -20,6 +20,7 @@ if myenv.get("HAVE_OPENSSL", 0) : 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", 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 @@ -4,8 +4,9 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ -#include "Swiften/TLS/Schannel/SchannelContext.h" -#include "Swiften/TLS/Schannel/SchannelCertificate.h" +#include +#include +#include namespace Swift { @@ -17,7 +18,7 @@ SchannelContext::SchannelContext() , 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 | @@ -517,22 +518,18 @@ void SchannelContext::encryptAndSendData(const SafeByteArray& data) //------------------------------------------------------------------------ -bool SchannelContext::setClientCertificate(CertificateWithKey * certificate) +bool SchannelContext::setClientCertificate(CertificateWithKey::ref certificate) { - if (!certificate || certificate->isNull()) { + boost::shared_ptr capiCertificate = boost::dynamic_pointer_cast(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; } //------------------------------------------------------------------------ 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 @@ -37,7 +37,7 @@ namespace Swift // 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); 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 @@ -11,10 +11,10 @@ #include #include +#include #include namespace Swift { - class CertificateWithKey; class TLSContext { public: @@ -22,7 +22,7 @@ namespace Swift { 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; -- cgit v0.10.2-6-g49f6