From d5f885dd9aa65d18145a99826a1c30aeb62aca8e Mon Sep 17 00:00:00 2001 From: Alexey Melnikov Date: Fri, 9 Mar 2012 20:19:19 +0000 Subject: Added support for determining SmartCard Reader associated with a certificate (if any) This patch implements monitoring for SmartCard ejection. This is done by periodically (currently every second) polling smart card reader for the smart card status. If the smart card status becomes "absent" or "unknown" (an error to query the smartcard), the TLS session is aborted. This usually results in an attempt to reestablish TLS which will pop up "please insert the smart card" dialog. License: This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details. diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp index dc6001b..7612720 100644 --- a/Swift/QtUI/QtLoginWindow.cpp +++ b/Swift/QtUI/QtLoginWindow.cpp @@ -49,7 +49,7 @@ namespace Swift{ -QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* settings) : QMainWindow(), settings_(settings) { +QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* settings, TimerFactory* timerFactory) : QMainWindow(), settings_(settings), timerFactory_(timerFactory) { uiEventStream_ = uiEventStream; setWindowTitle("Swift"); @@ -351,7 +351,7 @@ void QtLoginWindow::loginClicked() { std::string certificateString = Q2PSTRING(certificateFile_); #if defined(HAVE_SCHANNEL) if (isCAPIURI(certificateString)) { - certificate = boost::make_shared(certificateString); + certificate = boost::make_shared(certificateString, timerFactory_); } else { certificate = boost::make_shared(certificateString, createSafeByteArray(Q2PSTRING(password_->text()))); } diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h index dcd7c18..1add2f4 100644 --- a/Swift/QtUI/QtLoginWindow.h +++ b/Swift/QtUI/QtLoginWindow.h @@ -25,6 +25,8 @@ class QComboBox; namespace Swift { class SettingsProvider; + class TimerFactory; + class QtLoginWindow : public QMainWindow, public LoginWindow { Q_OBJECT public: @@ -35,7 +37,7 @@ namespace Swift { }; public: - QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* settings); + QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* settings, TimerFactory* timerFactory); void morphInto(MainWindow *mainWindow); virtual void loggedOut(); @@ -98,5 +100,6 @@ namespace Swift { SettingsProvider* settings_; QAction* xmlConsoleAction_; QAction* fileTransferOverviewAction_; + TimerFactory* timerFactory_; }; } diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index 9dabf21..60f93cc 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -172,7 +172,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa // Don't add the first tray (see note above) systemTrays_.push_back(new QtSystemTray()); } - QtUIFactory* uiFactory = new QtUIFactory(settingsHierachy_, qtSettings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, startMinimized); + QtUIFactory* uiFactory = new QtUIFactory(settingsHierachy_, qtSettings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, networkFactories_.getTimerFactory(), startMinimized); uiFactories_.push_back(uiFactory); MainController* mainController = new MainController( &clientMainThreadCaller_, diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index c686442..a8b693d 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -30,7 +30,7 @@ namespace Swift { -QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, bool startMinimized) : settings(settings), qtOnlySettings(qtOnlySettings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), lastMainWindow(NULL), loginWindow(NULL), startMinimized(startMinimized) { +QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, bool startMinimized) : settings(settings), qtOnlySettings(qtOnlySettings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), timerFactory_(timerFactory), lastMainWindow(NULL), loginWindow(NULL), startMinimized(startMinimized) { chatFontSize = settings->getSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE); } @@ -60,7 +60,7 @@ MainWindow* QtUIFactory::createMainWindow(UIEventStream* eventStream) { } LoginWindow* QtUIFactory::createLoginWindow(UIEventStream* eventStream) { - loginWindow = new QtLoginWindow(eventStream, settings); + loginWindow = new QtLoginWindow(eventStream, settings, timerFactory_); if (netbookSplitter) { netbookSplitter->insertWidget(0, loginWindow); } diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h index c9e2f2e..8b8e3ce 100644 --- a/Swift/QtUI/QtUIFactory.h +++ b/Swift/QtUI/QtUIFactory.h @@ -23,11 +23,12 @@ namespace Swift { class QtChatTheme; class QtChatWindowFactory; class QtChatWindow; + class TimerFactory; class QtUIFactory : public QObject, public UIFactory { Q_OBJECT public: - QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, bool startMinimized); + QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, bool startMinimized); virtual XMLConsoleWidget* createXMLConsoleWidget(); virtual MainWindow* createMainWindow(UIEventStream* eventStream); @@ -57,6 +58,7 @@ namespace Swift { QtMainWindow* lastMainWindow; QtLoginWindow* loginWindow; std::vector > chatWindows; + TimerFactory* timerFactory_; bool startMinimized; int chatFontSize; }; diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 8d7697a..0622cc6 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -40,7 +40,8 @@ if myenv.get("HAVE_SNARL", False) : myenv.UseFlags(myenv["SNARL_FLAGS"]) myenv.Append(CPPDEFINES = ["HAVE_SNARL"]) if env["PLATFORM"] == "win32" : - myenv.Append(LIBS = ["cryptui"]) + myenv.Append(LIBS = ["cryptui"]) + myenv.Append(LIBS = ["Winscard"]) myenv.UseFlags(myenv["PLATFORM_FLAGS"]) myenv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"]) diff --git a/Swiften/TLS/CAPICertificate.cpp b/Swiften/TLS/CAPICertificate.cpp index 0dc3009..b33ebcf 100644 --- a/Swiften/TLS/CAPICertificate.cpp +++ b/Swiften/TLS/CAPICertificate.cpp @@ -5,9 +5,11 @@ */ #pragma once +#include #include #include +#include #include // Size of the SHA1 hash @@ -15,15 +17,39 @@ namespace Swift { -CAPICertificate::CAPICertificate(const std::string& capiUri) - : valid_(false), uri_(capiUri), certStoreHandle_(0), certStore_(), certName_() { + +CAPICertificate::CAPICertificate(const std::string& capiUri, TimerFactory* timerFactory) + : valid_(false), + uri_(capiUri), + certStoreHandle_(0), + scardContext_(0), + cardHandle_(0), + certStore_(), + certName_(), + smartCardReaderName_(), + timerFactory_(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_); + } } bool CAPICertificate::isNull() const { @@ -38,6 +64,10 @@ const std::string& CAPICertificate::getCertName() const { return certName_; } +const std::string& CAPICertificate::getSmartCardReaderName() const { + return smartCardReaderName_; +} + PCCERT_CONTEXT findCertificateInStore (HCERTSTORE certStoreHandle, const std::string &certName) { PCCERT_CONTEXT pCertContext = NULL; @@ -170,6 +200,30 @@ void CAPICertificate::setUri (const std::string& capiUri) { return; } + + char smartcard_reader[1024]; + DWORD buflen; + + buflen = sizeof(smartcard_reader); + if (!CryptGetProvParam(hprov, PP_SMARTCARD_READER, (BYTE *)&smartcard_reader, &buflen, 0)) { + DWORD error; + + error = GetLastError(); + smartCardReaderName_ = ""; + } else { + LONG lRet; + + smartCardReaderName_ = smartcard_reader; + + lRet = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &scardContext_); + if (SCARD_S_SUCCESS == lRet) { + // Initiate monitoring for smartcard ejection + smartCardTimer_ = timerFactory_->createTimer(SMARTCARD_EJECTION_CHECK_FREQ); + } else { + ///Need to handle an error here + } + } + if (!CryptGetUserKey(hprov, pinfo->dwKeySpec, &key)) { CryptReleaseContext(hprov, 0); free(pinfo); @@ -180,7 +234,133 @@ void CAPICertificate::setUri (const std::string& capiUri) { CryptReleaseContext(hprov, 0); free(pinfo); + 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) { + LONG lReturn; + DWORD dwAP; + char szReader[200]; + DWORD cch = sizeof(szReader); + BYTE bAttr[32]; + DWORD cByte = 32; + + if (hCardHandle == 0) { + lReturn = SCardConnect(hContext, + pReader, + SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, + &hCardHandle, + &dwAP); + if ( SCARD_S_SUCCESS != lReturn ) { + hCardHandle = 0; + if (SCARD_E_NO_SMARTCARD == lReturn || SCARD_W_REMOVED_CARD == lReturn) { + *pdwState = SCARD_ABSENT; + } else { + *pdwState = SCARD_UNKNOWN; + } + goto done; + } + } + + lReturn = SCardStatus(hCardHandle, + szReader, // Unfortunately we can't use NULL here + &cch, + pdwState, + NULL, + (LPBYTE)&bAttr, + &cByte); + + if ( SCARD_S_SUCCESS != lReturn ) { + if (SCARD_E_NO_SMARTCARD == lReturn || SCARD_W_REMOVED_CARD == lReturn) { + *pdwState = SCARD_ABSENT; + } else { + *pdwState = SCARD_UNKNOWN; + } + } + +done: + if (newCardHandle == NULL) { + (void) SCardDisconnect(hCardHandle, SCARD_LEAVE_CARD); + hCardHandle = 0; + } else { + *newCardHandle = hCardHandle; + } +} + +bool CAPICertificate::checkIfSmartCardPresent () { + + DWORD dwState; + + if (!smartCardReaderName_.empty()) { + smartcard_check_status (scardContext_, + smartCardReaderName_.c_str(), + cardHandle_, + &cardHandle_, + &dwState); +////DEBUG + switch ( dwState ) { + case SCARD_ABSENT: + printf("Card absent.\n"); + break; + case SCARD_PRESENT: + printf("Card present.\n"); + break; + case SCARD_SWALLOWED: + printf("Card swallowed.\n"); + break; + case SCARD_POWERED: + printf("Card has power.\n"); + break; + case SCARD_NEGOTIABLE: + printf("Card reset and waiting PTS negotiation.\n"); + break; + case SCARD_SPECIFIC: + printf("Card has specific communication protocols set.\n"); + break; + default: + printf("Unknown or unexpected card state.\n"); + 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() { + + if (checkIfSmartCardPresent() == false) { + smartCardTimer_->stop(); + onCertificateCardRemoved(); + } else { + smartCardTimer_->start(); + } +} + } diff --git a/Swiften/TLS/CAPICertificate.h b/Swiften/TLS/CAPICertificate.h index 4204a6b..c8c00fe 100644 --- a/Swiften/TLS/CAPICertificate.h +++ b/Swiften/TLS/CAPICertificate.h @@ -6,17 +6,26 @@ #pragma once +#include #include #include +#include #define SECURITY_WIN32 #include #include +#include + +/* In ms */ +#define SMARTCARD_EJECTION_CHECK_FREQ 1000 namespace Swift { + class TimerFactory; + class CAPICertificate : public Swift::CertificateWithKey { public: - CAPICertificate(const std::string& capiUri); +////Allow timerFactory to be NULL? + CAPICertificate(const std::string& capiUri, TimerFactory* timerFactory); virtual ~CAPICertificate(); @@ -26,18 +35,32 @@ namespace Swift { const std::string& getCertName() const; + const std::string& getSmartCardReaderName() const; + + public: + boost::signal onCertificateCardRemoved; + private: void setUri (const std::string& capiUri); + void handleSmartCardTimerTick(); + + bool checkIfSmartCardPresent(); + private: bool valid_; std::string uri_; HCERTSTORE certStoreHandle_; + SCARDCONTEXT scardContext_; + SCARDHANDLE cardHandle_; /* Parsed components of the uri_ */ std::string certStore_; std::string certName_; + std::string smartCardReaderName_; + boost::shared_ptr smartCardTimer_; + TimerFactory* timerFactory_; }; PCCERT_CONTEXT findCertificateInStore (HCERTSTORE certStoreHandle, const std::string &certName); diff --git a/Swiften/TLS/Schannel/SchannelContext.cpp b/Swiften/TLS/Schannel/SchannelContext.cpp index 4f8f36f..8e952ea 100644 --- a/Swiften/TLS/Schannel/SchannelContext.cpp +++ b/Swiften/TLS/Schannel/SchannelContext.cpp @@ -4,8 +4,10 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ -#include "Swiften/TLS/Schannel/SchannelContext.h" -#include "Swiften/TLS/Schannel/SchannelCertificate.h" +#include + +#include +#include #include #include // For SECURITY_FLAG_IGNORE_CERT_CN_INVALID @@ -19,6 +21,7 @@ SchannelContext::SchannelContext() , m_my_cert_store(NULL) , m_cert_store_name("MY") , m_cert_name() +, m_smartcard_reader() { m_ctxtFlags = ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | @@ -639,10 +642,21 @@ bool SchannelContext::setClientCertificate(CertificateWithKey::ref certificate) // are valid at this point m_cert_store_name = capiCertificate->getCertStoreName(); m_cert_name = capiCertificate->getCertName(); +////At the moment this is only useful for logging: + m_smartcard_reader = capiCertificate->getSmartCardReaderName(); + + capiCertificate->onCertificateCardRemoved.connect(boost::bind(&SchannelContext::handleCertificateCardRemoved, this)); + return true; } //------------------------------------------------------------------------ +void SchannelContext::handleCertificateCardRemoved() { + //ToDo: Might want to log the reason ("certificate card ejected") + indicateError(); +} + +//------------------------------------------------------------------------ Certificate::ref SchannelContext::getPeerCertificate() const { diff --git a/Swiften/TLS/Schannel/SchannelContext.h b/Swiften/TLS/Schannel/SchannelContext.h index 70b0694..bce7415 100644 --- a/Swiften/TLS/Schannel/SchannelContext.h +++ b/Swiften/TLS/Schannel/SchannelContext.h @@ -10,7 +10,7 @@ #include "Swiften/TLS/TLSContext.h" #include "Swiften/TLS/Schannel/SchannelUtil.h" -#include +#include "Swiften/TLS/CertificateWithKey.h" #include "Swiften/Base/ByteArray.h" #define SECURITY_WIN32 @@ -62,6 +62,8 @@ namespace Swift void appendNewData(const SafeByteArray& data); SECURITY_STATUS validateServerCertificate(); + void handleCertificateCardRemoved(); + private: enum SchannelState { @@ -86,5 +88,7 @@ namespace Swift HCERTSTORE m_my_cert_store; std::string m_cert_store_name; std::string m_cert_name; +////Not needed, most likely + std::string m_smartcard_reader; //Can be empty string for non SmartCard certificates }; } -- cgit v0.10.2-6-g49f6