diff options
36 files changed, 320 insertions, 3 deletions
diff --git a/BuildTools/SCons/SConscript.boot b/BuildTools/SCons/SConscript.boot index a3a5c6d..098df0a 100644 --- a/BuildTools/SCons/SConscript.boot +++ b/BuildTools/SCons/SConscript.boot @@ -209,19 +209,19 @@ if env.get("coverage", 0) : if env["PLATFORM"] == "win32" : env.Append(LIBS = ["user32", "crypt32", "dnsapi", "iphlpapi", "ws2_32", "wsock32", "Advapi32"]) env.Append(CCFLAGS = ["/EHsc", "/nologo"]) # FIXME: We should find a decent solution for MSVS 10 if int(env["MSVS_VERSION"].split(".")[0]) < 10 : env["LINKCOM"] = [env["LINKCOM"], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;1'] env["SHLINKCOM"] = [env["SHLINKCOM"], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;2'] if env["PLATFORM"] == "darwin" and not env["target"] in ["iphone-device", "iphone-simulator", "xcode"] : - env.Append(FRAMEWORKS = ["IOKit", "AppKit", "SystemConfiguration"]) + env.Append(FRAMEWORKS = ["IOKit", "AppKit", "SystemConfiguration", "Security", "SecurityInterface"]) # Testing env["TEST_TYPE"] = env["test"] if "check" in ARGUMENTS : env["TEST_TYPE"] = "unit" env["checker_report"] = ARGUMENTS.get("checker_report", False) env["TEST"] = (env["TEST_TYPE"] != "none") or env.GetOption("clean") if env.get("valgrind", 0) : env["TEST_RUNNER"] = "valgrind --suppressions=QA/valgrind.supp -q --leak-check=full --track-origins=yes " diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 7bd89cb..40b4ded 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -66,18 +66,19 @@ #include <Swift/Controllers/ProfileController.h> #include <Swift/Controllers/ContactEditController.h> #include <Swift/Controllers/XMPPURIController.h> #include "Swift/Controllers/AdHocManager.h" #include <SwifTools/Idle/IdleDetector.h> #include <Swift/Controllers/FileTransfer/FileTransferOverview.h> #include <Swiften/FileTransfer/FileTransferManager.h> #include <Swiften/Client/ClientXMLTracer.h> #include <Swift/Controllers/SettingConstants.h> +#include <Swiften/Client/StanzaChannel.h> namespace Swift { static const std::string CLIENT_NAME = "Swift"; static const std::string CLIENT_NODE = "http://swift.im"; MainController::MainController( EventLoop* eventLoop, @@ -279,18 +280,19 @@ void MainController::handleConnected() { profileController_ = new ProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_); srand(time(NULL)); int randomPort = 10000 + rand() % 10000; client_->getFileTransferManager()->startListeningOnPort(randomPort); ftOverview_ = new FileTransferOverview(client_->getFileTransferManager()); fileTransferListController_->setFileTransferOverview(ftOverview_); rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_); rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2)); rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this)); + rosterController_->getWindow()->onShowCertificateRequest.connect(boost::bind(&MainController::handleShowCertificateRequest, this)); contactEditController_ = new ContactEditController(rosterController_, client_->getVCardManager(), uiFactory_, uiEventStream_); chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_); client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1)); chatsManager_->setAvatarManager(client_->getAvatarManager()); eventWindowController_ = new EventWindowController(eventController_, uiFactory_); @@ -321,18 +323,19 @@ void MainController::handleConnected() { client_->requestRoster(); GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(JID(boundJID_.getDomain()), client_->getIQRouter()); discoInfoRequest->onResponse.connect(boost::bind(&MainController::handleServerDiscoInfoResponse, this, _1, _2)); discoInfoRequest->send(); client_->getVCardManager()->requestOwnVCard(); rosterController_->setEnabled(true); + rosterController_->getWindow()->setStreamEncryptionStatus(client_->isStreamEncrypted()); profileController_->setAvailable(true); contactEditController_->setAvailable(true); /* Send presence later to catch all the incoming presences. */ sendPresence(statusTracker_->getNextPresence()); /* Enable chats last of all, so rejoining MUCs has the right sent presence */ chatsManager_->setOnline(true); } @@ -411,18 +414,23 @@ void MainController::handleInputIdleChanged(bool idle) { if (statusTracker_->goAutoUnAway()) { if (client_ && client_->isAvailable()) { sendPresence(statusTracker_->getNextPresence()); } } } } } +void MainController::handleShowCertificateRequest() { + std::vector<Certificate::ref> chain = client_->getStanzaChannel()->getPeerCertificateChain(); + rosterController_->getWindow()->openCertificateDialog(chain); +} + 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'")); loginWindow_->setIsLoggingIn(false); } else { loginWindow_->setMessage(""); loginWindow_->setIsLoggingIn(true); profileSettings_ = new ProfileSettingsProvider(username, settings_); diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index 14de4eb..eeba9f3 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -97,18 +97,19 @@ namespace Swift { void handleChangeStatusRequest(StatusShow::Type show, const std::string &statusText); void handleDisconnected(const boost::optional<ClientError>& error); void handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo>, ErrorPayload::ref); void handleEventQueueLengthChange(int count); void handleVCardReceived(const JID& j, VCard::ref vCard); void handleSettingChanged(const std::string& settingPath); void handlePurgeSavedLoginRequest(const std::string& username); void sendPresence(boost::shared_ptr<Presence> presence); void handleInputIdleChanged(bool); + void handleShowCertificateRequest(); void logout(); void signOut(); void setReconnectTimer(); void resetPendingReconnects(); void resetCurrentError(); void performLoginFromCachedCredentials(); void reconnectAfterError(); void setManagersOffline(); diff --git a/Swift/Controllers/UIInterfaces/MainWindow.h b/Swift/Controllers/UIInterfaces/MainWindow.h index 93584e7..23328b4 100644 --- a/Swift/Controllers/UIInterfaces/MainWindow.h +++ b/Swift/Controllers/UIInterfaces/MainWindow.h @@ -4,19 +4,19 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <string> #include "Swiften/JID/JID.h" #include "Swiften/Elements/StatusShow.h" #include "Swiften/Elements/DiscoItems.h" - +#include "Swiften/TLS/Certificate.h" #include "Swiften/Base/boost_bsignals.h" #include <boost/shared_ptr.hpp> namespace Swift { class Roster; class MainWindow { public: MainWindow(bool candelete = true) : canDelete_(candelete) {} @@ -29,17 +29,20 @@ namespace Swift { virtual void setMyNick(const std::string& name) = 0; virtual void setMyJID(const JID& jid) = 0; virtual void setMyAvatarPath(const std::string& path) = 0; virtual void setMyStatusText(const std::string& status) = 0; virtual void setMyStatusType(StatusShow::Type type) = 0; /** Must be able to cope with NULL to clear the roster */ virtual void setRosterModel(Roster* roster) = 0; virtual void setConnecting() = 0; virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) = 0; + virtual void setStreamEncryptionStatus(bool tlsInPlaceAndValid) = 0; + virtual void openCertificateDialog(const std::vector<Certificate::ref>& chain) = 0; boost::signal<void (StatusShow::Type, const std::string&)> onChangeStatusRequest; boost::signal<void ()> onSignOutRequest; + boost::signal<void ()> onShowCertificateRequest; private: bool canDelete_; }; } diff --git a/Swift/Controllers/UnitTest/MockMainWindow.h b/Swift/Controllers/UnitTest/MockMainWindow.h index f773062..be1a932 100644 --- a/Swift/Controllers/UnitTest/MockMainWindow.h +++ b/Swift/Controllers/UnitTest/MockMainWindow.h @@ -16,13 +16,15 @@ namespace Swift { virtual ~MockMainWindow() {}; virtual void setRosterModel(Roster* roster) {this->roster = roster;}; virtual void setMyNick(const std::string& /*name*/) {};; virtual void setMyJID(const JID& /*jid*/) {};; virtual void setMyAvatarPath(const std::string& /*path*/) {}; virtual void setMyStatusText(const std::string& /*status*/) {}; virtual void setMyStatusType(StatusShow::Type /*type*/) {}; virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& /*commands*/) {}; virtual void setConnecting() {}; + virtual void setStreamEncryptionStatus(bool /*tlsInPlaceAndValid*/) {} + virtual void openCertificateDialog(const std::vector<Certificate::ref>& /*chain*/) {} Roster* roster; }; } diff --git a/Swift/QtUI/CocoaUIHelpers.h b/Swift/QtUI/CocoaUIHelpers.h new file mode 100644 index 0000000..25da0e3 --- /dev/null +++ b/Swift/QtUI/CocoaUIHelpers.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/TLS/Certificate.h> +#include <QWidget> + +namespace Swift { + +class CocoaUIHelpers { +public: + static void displayCertificateChainAsSheet(QWidget* parent, const std::vector<Certificate::ref>& chain); +}; + +} + diff --git a/Swift/QtUI/CocoaUIHelpers.mm b/Swift/QtUI/CocoaUIHelpers.mm new file mode 100644 index 0000000..3cb62f3 --- /dev/null +++ b/Swift/QtUI/CocoaUIHelpers.mm @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "CocoaUIHelpers.h" + +#include <boost/shared_ptr.hpp> +#include <boost/type_traits.hpp> + +#include <Cocoa/Cocoa.h> + +#include <Security/Security.h> +#include <SecurityInterface/SFCertificatePanel.h> + +#include <Swiften/Base/foreach.h> + +#pragma GCC diagnostic ignored "-Wold-style-cast" + +namespace Swift { + +void CocoaUIHelpers::displayCertificateChainAsSheet(QWidget* parent, const std::vector<Certificate::ref>& chain) { + NSWindow* parentWindow = [((NSView*)parent->winId()) window]; + NSMutableArray* certificates = [[NSMutableArray alloc] init]; + foreach(Certificate::ref cert, chain) { + // convert chain to SecCertificateRef + ByteArray certAsDER = cert->toDER(); + boost::shared_ptr<boost::remove_pointer<CFDataRef>::type> certData(CFDataCreate(NULL, certAsDER.data(), certAsDER.size()), CFRelease); + boost::shared_ptr<OpaqueSecCertificateRef> macCert(SecCertificateCreateWithData(NULL, certData.get()), CFRelease); + + // add to NSMutable array + [certificates addObject: (id)macCert.get()]; + } + + + SFCertificatePanel* panel = [[SFCertificatePanel alloc] init]; + //[panel setPolicies:(id)policies.get()]; + [panel beginSheetForWindow:parentWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL certificates:certificates showGroup:YES]; + [certificates release]; +} + +} diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 9a3ce6b..547f22b 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -28,32 +28,40 @@ #include <Roster/QtRosterWidget.h> #include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h> #include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h> #include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h> #include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h> #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> #include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h> #include <Swift/QtUI/QtUISettingConstants.h> #include <Swift/Controllers/SettingConstants.h> +#include <Swiften/Base/Platform.h> + +#if defined(SWIFTEN_PLATFORM_MACOSX) +#include <Swift/QtUI/CocoaUIHelpers.h> +#elif defined(SWIFTEN_PLATFORM_WINDOWS) +#include <Swift/QtUI/WinUIHelpers.h> +#endif namespace Swift { QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStream, QtLoginWindow::QtMenus loginMenus) : QWidget(), MainWindow(false), loginMenus_(loginMenus) { uiEventStream_ = uiEventStream; settings_ = settings; setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this); mainLayout->setContentsMargins(0,0,0,0); mainLayout->setSpacing(0); meView_ = new QtRosterHeader(settings, this); mainLayout->addWidget(meView_); connect(meView_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleStatusChanged(StatusShow::Type, const QString&))); connect(meView_, SIGNAL(onEditProfileRequest()), this, SLOT(handleEditProfileRequest())); + connect(meView_, SIGNAL(onShowCertificateInfo()), this, SLOT(handleShowCertificateInfo())); tabs_ = new QtTabWidget(this); #if QT_VERSION >= 0x040500 tabs_->setDocumentMode(true); #endif tabs_->setTabPosition(QTabWidget::South); mainLayout->addWidget(tabs_); contactsTabWidget_ = new QWidget(this); contactsTabWidget_->setContentsMargins(0, 0, 0, 0); @@ -147,18 +155,22 @@ QtMainWindow::~QtMainWindow() { void QtMainWindow::handleTabChanged(int index) { settings_->storeSetting(QtUISettingConstants::CURRENT_ROSTER_TAB, index); } void QtMainWindow::handleToggleRequestDeliveryReceipts(bool enabled) { settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, enabled); } +void QtMainWindow::handleShowCertificateInfo() { + onShowCertificateRequest(); +} + QtEventWindow* QtMainWindow::getEventWindow() { return eventWindow_; } QtChatListWindow* QtMainWindow::getChatListWindow() { return chatListWindow_; } void QtMainWindow::setRosterModel(Roster* roster) { @@ -250,18 +262,32 @@ void QtMainWindow::setMyStatusText(const std::string& status) { void QtMainWindow::setMyStatusType(StatusShow::Type type) { meView_->setStatusType(type); } void QtMainWindow::setConnecting() { meView_->setConnecting(); } +void QtMainWindow::setStreamEncryptionStatus(bool tlsInPlaceAndValid) { + meView_->setStreamEncryptionStatus(tlsInPlaceAndValid); +} + +void QtMainWindow::openCertificateDialog(const std::vector<Certificate::ref>& chain) { +#if defined(SWIFTEN_PLATFORM_MACOSX) + CocoaUIHelpers::displayCertificateChainAsSheet(this, chain); +#elif defined(SWIFTEN_PLATFORM_WINDOWS) + WinUIHelpers::displayCertificateChainAsSheet(this,chain); +#else +#pragma message ("No certificate dialog available on this platform.") +#endif +} + void QtMainWindow::handleAdHocActionTriggered(bool /*checked*/) { QAction* action = qobject_cast<QAction*>(sender()); assert(action); DiscoItems::Item command = serverAdHocCommands_[serverAdHocCommandActions_.indexOf(action)]; uiEventStream_->send(boost::shared_ptr<UIEvent>(new RequestAdHocUIEvent(command))); } void QtMainWindow::setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) { serverAdHocCommands_ = commands; diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h index bef483d..c725d08 100644 --- a/Swift/QtUI/QtMainWindow.h +++ b/Swift/QtUI/QtMainWindow.h @@ -39,18 +39,20 @@ namespace Swift { QtMainWindow(SettingsProvider*, UIEventStream* eventStream, QtLoginWindow::QtMenus loginMenus); virtual ~QtMainWindow(); std::vector<QMenu*> getMenus() {return menus_;} void setMyNick(const std::string& name); void setMyJID(const JID& jid); void setMyAvatarPath(const std::string& path); void setMyStatusText(const std::string& status); void setMyStatusType(StatusShow::Type type); void setConnecting(); + void setStreamEncryptionStatus(bool tlsInPlaceAndValid); + void openCertificateDialog(const std::vector<Certificate::ref>& chain); QtEventWindow* getEventWindow(); QtChatListWindow* getChatListWindow(); void setRosterModel(Roster* roster); void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands); private slots: void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage); void handleSettingChanged(const std::string& settingPath); void handleShowOfflineToggled(bool); void handleJoinMUCAction(); @@ -58,18 +60,19 @@ namespace Swift { void handleEditProfileAction(); void handleAddUserActionTriggered(bool checked); void handleChatUserActionTriggered(bool checked); void handleAdHocActionTriggered(bool checked); void handleEventCountUpdated(int count); void handleChatCountUpdated(int count); void handleEditProfileRequest(); void handleTabChanged(int index); void handleToggleRequestDeliveryReceipts(bool enabled); + void handleShowCertificateInfo(); private: SettingsProvider* settings_; QtLoginWindow::QtMenus loginMenus_; std::vector<QMenu*> menus_; QtRosterWidget* treeWidget_; QtRosterHeader* meView_; QAction* addUserAction_; QAction* editUserAction_; diff --git a/Swift/QtUI/QtRosterHeader.cpp b/Swift/QtUI/QtRosterHeader.cpp index 98e75c2..2bce222 100644 --- a/Swift/QtUI/QtRosterHeader.cpp +++ b/Swift/QtUI/QtRosterHeader.cpp @@ -39,26 +39,39 @@ QtRosterHeader::QtRosterHeader(SettingsProvider* settings, QWidget* parent) : QW avatarLabel_->setScaledContents(false); topLayout->addWidget(avatarLabel_); connect(avatarLabel_, SIGNAL(clicked()), this, SIGNAL(onEditProfileRequest())); QVBoxLayout* rightLayout = new QVBoxLayout(); rightLayout->setSpacing(4); rightLayout->setContentsMargins(4,0,0,0); topLayout->addLayout(rightLayout); + QHBoxLayout* nameAndSecurityLayout = new QHBoxLayout(); + nameAndSecurityLayout->setContentsMargins(4,0,0,0); + nameWidget_ = new QtNameWidget(settings, this); connect(nameWidget_, SIGNAL(onChangeNickRequest()), this, SIGNAL(onEditProfileRequest())); - rightLayout->addWidget(nameWidget_); + nameAndSecurityLayout->addWidget(nameWidget_); + + securityInfoButton_ = new QToolButton(this); + + securityInfoButton_->setStyleSheet("border: none; hover: {border: 1px} pressed {border: 1px}"); + // TODO: replace with a more appropriate icon + securityInfoButton_->setIcon(QIcon(":/icons/certificate.png")); + connect(securityInfoButton_, SIGNAL(clicked()), this, SIGNAL(onShowCertificateInfo())); + nameAndSecurityLayout->addWidget(securityInfoButton_); + rightLayout->addLayout(nameAndSecurityLayout); statusWidget_ = new QtStatusWidget(this); connect(statusWidget_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleChangeStatusRequest(StatusShow::Type, const QString&))); rightLayout->addWidget(statusWidget_); + show(); } void QtRosterHeader::handleChangeStatusRequest(StatusShow::Type type, const QString& text) { emit onChangeStatusRequest(type, text); } void QtRosterHeader::setStatusText(const QString& statusMessage) { statusWidget_->setStatusText(statusMessage); @@ -66,18 +79,22 @@ void QtRosterHeader::setStatusText(const QString& statusMessage) { void QtRosterHeader::setStatusType(StatusShow::Type type) { statusWidget_->setStatusType(type); } void QtRosterHeader::setConnecting() { statusWidget_->setConnecting(); } +void QtRosterHeader::setStreamEncryptionStatus(bool tlsInPlace) { + securityInfoButton_->setVisible(tlsInPlace); +} + void QtRosterHeader::setAvatar(const QString& path) { QString scaledAvatarPath = QtScaledAvatarCache(avatarSize_).getScaledAvatarPath(path); QPixmap avatar; if (QFileInfo(scaledAvatarPath).exists()) { avatar.load(scaledAvatarPath); } else { avatar = QPixmap(":/icons/avatar.png").scaled(avatarSize_, avatarSize_, Qt::KeepAspectRatio, Qt::SmoothTransformation); } diff --git a/Swift/QtUI/QtRosterHeader.h b/Swift/QtUI/QtRosterHeader.h index 050460c..1132ee3 100644 --- a/Swift/QtUI/QtRosterHeader.h +++ b/Swift/QtUI/QtRosterHeader.h @@ -5,18 +5,19 @@ */ #pragma once #include <QWidget> #include <QLabel> #include <QPixmap> #include <QSize> #include <QToolBar> +#include <QToolButton> #include <string> #include "Swiften/Elements/StatusShow.h" #include "QtTextEdit.h" class QHBoxLayout; namespace Swift { @@ -31,25 +32,28 @@ namespace Swift { QtRosterHeader(SettingsProvider* settings, QWidget* parent = NULL); void setAvatar(const QString& path); void setJID(const QString& jid); void setNick(const QString& nick); void setStatusText(const QString& statusMessage); void setStatusType(StatusShow::Type type); void setConnecting(); + void setStreamEncryptionStatus(bool tlsInPlace); signals: void onChangeStatusRequest(StatusShow::Type showType, const QString &statusMessage); void onEditProfileRequest(); + void onShowCertificateInfo(); private slots: void handleChangeStatusRequest(StatusShow::Type type, const QString &statusMessage); private: QString name_; QtClickableLabel* avatarLabel_; QtNameWidget* nameWidget_; QtTextEdit* statusEdit_; QToolBar* toolBar_; QtStatusWidget* statusWidget_; + QToolButton* securityInfoButton_; static const int avatarSize_; }; } diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index f944b43..4c53313 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -155,31 +155,33 @@ version_match = re.match("(\d+)\.(\d+).*", myenv["SWIFT_VERSION"]) myenv["SWIFT_VERSION_MAJOR"] = int(version_match.group(1)) if version_match else 0 myenv["SWIFT_VERSION_MINOR"] = int(version_match.group(2)) if version_match else 0 if env["PLATFORM"] == "win32" : res = myenv.RES("#/Swift/resources/Windows/Swift.rc") # For some reason, SCons isn't picking up the dependency correctly # Adding it explicitly until i figure out why myenv.Depends(res, "../Controllers/BuildVersion.h") sources += [ + "WinUIHelpers.cpp", "CAPICertificateSelector.cpp", "WindowsNotifier.cpp", "#/Swift/resources/Windows/Swift.res" ] if env["PLATFORM"] == "posix" : sources += [ "FreeDesktopNotifier.cpp", "QtDBUSURIHandler.cpp", ] if env["PLATFORM"] == "darwin" : sources += ["CocoaApplicationActivateHelper.mm"] + sources += ["CocoaUIHelpers.mm"] if env["PLATFORM"] == "darwin" or env["PLATFORM"] == "win32" : swiftProgram = myenv.Program("Swift", sources) else : swiftProgram = myenv.Program("swift", sources) if env["PLATFORM"] != "darwin" and env["PLATFORM"] != "win32" : openURIProgram = myenv.Program("swift-open-uri", "swift-open-uri.cpp") else : diff --git a/Swift/QtUI/WinUIHelpers.cpp b/Swift/QtUI/WinUIHelpers.cpp new file mode 100644 index 0000000..edd1120 --- /dev/null +++ b/Swift/QtUI/WinUIHelpers.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "WinUIHelpers.h" + +#include <windows.h> +#include <Wincrypt.h> +#include <cryptuiapi.h> +#pragma comment(lib, "cryptui.lib") + +#include <boost/shared_ptr.hpp> + +#include <Swiften/Base/foreach.h> + +namespace Swift { + +void WinUIHelpers::displayCertificateChainAsSheet(QWidget* parent, const std::vector<Certificate::ref>& chain) { + if (chain.empty()) { + return; + } + + // create certificate store to store the certificate chain in + HCERTSTORE chainStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL); + if (!chainStore) { + return; + } + + ByteArray certAsDER = chain[0]->toDER(); + boost::shared_ptr<const CERT_CONTEXT> certificate_chain; + { + PCCERT_CONTEXT certChain; + BOOL ok = CertAddCertificateContextToStore(chainStore, CertCreateCertificateContext(X509_ASN_ENCODING, certAsDER.data(), certAsDER.size()), CERT_STORE_ADD_ALWAYS, &certChain); + // maybe free the cert contex we created + if (!ok || !certChain) { + return; + } + certificate_chain.reset(certChain, CertFreeCertificateContext); + } + + for (size_t i = 1; i < chain.size(); ++i) { + ByteArray certAsDER = chain[i]->toDER(); + CertAddCertificateContextToStore(chainStore, CertCreateCertificateContext(X509_ASN_ENCODING, certAsDER.data(), certAsDER.size()), CERT_STORE_ADD_ALWAYS, NULL); + } + + CRYPTUI_VIEWCERTIFICATE_STRUCT viewDialogProperties = { 0 }; + viewDialogProperties.dwSize = sizeof(viewDialogProperties); + viewDialogProperties.hwndParent = parent->winId(); + viewDialogProperties.dwFlags = CRYPTUI_DISABLE_EDITPROPERTIES | CRYPTUI_DISABLE_ADDTOSTORE | CRYPTUI_ENABLE_REVOCATION_CHECKING; + viewDialogProperties.pCertContext = certificate_chain.get(); + viewDialogProperties.cStores = 1; + viewDialogProperties.rghStores = &chainStore; + BOOL properties_changed; + + // blocking call that shows modal certificate dialog + BOOL rv = ::CryptUIDlgViewCertificate(&viewDialogProperties, &properties_changed); +} + +} diff --git a/Swift/QtUI/WinUIHelpers.h b/Swift/QtUI/WinUIHelpers.h new file mode 100644 index 0000000..d34d236 --- /dev/null +++ b/Swift/QtUI/WinUIHelpers.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/TLS/Certificate.h> +#include <QWidget> + +namespace Swift { + +class WinUIHelpers { +public: + static void displayCertificateChainAsSheet(QWidget* parent, const std::vector<Certificate::ref>& chain); +}; + +} + diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h index 2205c8d..b67b23d 100644 --- a/Swiften/Client/ClientSession.h +++ b/Swiften/Client/ClientSession.h @@ -92,18 +92,22 @@ namespace Swift { bool getStreamManagementEnabled() const { return stanzaAckRequester_; } bool getRosterVersioningSupported() const { return rosterVersioningSupported; } + std::vector<Certificate::ref> getPeerCertificateChain() const { + return stream->getPeerCertificateChain(); + } + const JID& getLocalJID() const { return localJID; } void start(); void finish(); bool isFinished() const { return getState() == Finished; diff --git a/Swiften/Client/ClientSessionStanzaChannel.cpp b/Swiften/Client/ClientSessionStanzaChannel.cpp index 5dc0a42..8d85953 100644 --- a/Swiften/Client/ClientSessionStanzaChannel.cpp +++ b/Swiften/Client/ClientSessionStanzaChannel.cpp @@ -76,18 +76,25 @@ void ClientSessionStanzaChannel::handleStanza(boost::shared_ptr<Stanza> stanza) bool ClientSessionStanzaChannel::getStreamManagementEnabled() const { if (session) { return session->getStreamManagementEnabled(); } return false; } +std::vector<Certificate::ref> ClientSessionStanzaChannel::getPeerCertificateChain() const { + if (session) { + return session->getPeerCertificateChain(); + } + return std::vector<Certificate::ref>(); +} + void ClientSessionStanzaChannel::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) { onStanzaAcked(stanza); } void ClientSessionStanzaChannel::handleSessionInitialized() { onAvailableChanged(true); } diff --git a/Swiften/Client/ClientSessionStanzaChannel.h b/Swiften/Client/ClientSessionStanzaChannel.h index 47fb50e..2743a16 100644 --- a/Swiften/Client/ClientSessionStanzaChannel.h +++ b/Swiften/Client/ClientSessionStanzaChannel.h @@ -21,18 +21,19 @@ namespace Swift { */ class ClientSessionStanzaChannel : public StanzaChannel { public: void setSession(boost::shared_ptr<ClientSession> session); void sendIQ(boost::shared_ptr<IQ> iq); void sendMessage(boost::shared_ptr<Message> message); void sendPresence(boost::shared_ptr<Presence> presence); bool getStreamManagementEnabled() const; + virtual std::vector<Certificate::ref> getPeerCertificateChain() const; bool isAvailable() const { return session && session->getState() == ClientSession::Initialized; } private: std::string getNewIQID(); void send(boost::shared_ptr<Stanza> stanza); void handleSessionFinished(boost::shared_ptr<Error> error); diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp index 8a922ba..36e27eb 100644 --- a/Swiften/Client/CoreClient.cpp +++ b/Swiften/Client/CoreClient.cpp @@ -354,18 +354,22 @@ void CoreClient::handleStanzaAcked(Stanza::ref stanza) { bool CoreClient::isAvailable() const { return stanzaChannel_->isAvailable(); } bool CoreClient::getStreamManagementEnabled() const { return stanzaChannel_->getStreamManagementEnabled(); } +bool CoreClient::isStreamEncrypted() const { + return sessionStream_->isTLSEncrypted(); +} + StanzaChannel* CoreClient::getStanzaChannel() const { return stanzaChannel_; } const JID& CoreClient::getJID() const { if (session_) { return session_->getLocalJID(); } else { diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h index cafc634..985bf7f 100644 --- a/Swiften/Client/CoreClient.h +++ b/Swiften/Client/CoreClient.h @@ -121,18 +121,23 @@ namespace Swift { * Checks whether stream management is enabled. * * If stream management is enabled, onStanzaAcked will be * emitted when a stanza is received by the server. * * \see onStanzaAcked */ bool getStreamManagementEnabled() const; + /** + * Checks whether stream encryption (TLS) is currently active. + */ + bool isStreamEncrypted() const; + StanzaChannel* getStanzaChannel() const; /** * Sets the certificate trust checker. * * This checker will be called when the server sends a * TLS certificate that does not validate. If the trust checker * says the certificate is trusted, then connecting will proceed; * if not, the connection will end with an error. diff --git a/Swiften/Client/DummyStanzaChannel.h b/Swiften/Client/DummyStanzaChannel.h index c2f3919..5cdedba 100644 --- a/Swiften/Client/DummyStanzaChannel.h +++ b/Swiften/Client/DummyStanzaChannel.h @@ -73,13 +73,17 @@ namespace Swift { } template<typename T> boost::shared_ptr<T> getStanzaAtIndex(size_t index) { if (sentStanzas.size() <= index) { return boost::shared_ptr<T>(); } return boost::dynamic_pointer_cast<T>(sentStanzas[index]); } + std::vector<Certificate::ref> getPeerCertificateChain() const { + return std::vector<Certificate::ref>(); + } + std::vector<boost::shared_ptr<Stanza> > sentStanzas; bool available_; }; } diff --git a/Swiften/Client/StanzaChannel.h b/Swiften/Client/StanzaChannel.h index f1d76e0..5e85d3c 100644 --- a/Swiften/Client/StanzaChannel.h +++ b/Swiften/Client/StanzaChannel.h @@ -6,24 +6,26 @@ #pragma once #include <Swiften/Base/boost_bsignals.h> #include <boost/shared_ptr.hpp> #include <Swiften/Queries/IQChannel.h> #include <Swiften/Elements/Message.h> #include <Swiften/Elements/Presence.h> +#include <Swiften/TLS/Certificate.h> namespace Swift { class StanzaChannel : public IQChannel { public: virtual void sendMessage(boost::shared_ptr<Message>) = 0; virtual void sendPresence(boost::shared_ptr<Presence>) = 0; virtual bool isAvailable() const = 0; virtual bool getStreamManagementEnabled() const = 0; + virtual std::vector<Certificate::ref> getPeerCertificateChain() const = 0; boost::signal<void (bool /* isAvailable */)> onAvailableChanged; boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived; boost::signal<void (boost::shared_ptr<Presence>) > onPresenceReceived; boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaAcked; }; } diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp index 6793643..d1ca70a 100644 --- a/Swiften/Client/UnitTest/ClientSessionTest.cpp +++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp @@ -393,18 +393,22 @@ class ClientSessionTest : public CppUnit::TestFixture { virtual ByteArray getTLSFinishMessage() const { return ByteArray(); } virtual Certificate::ref getPeerCertificate() const { return Certificate::ref(new SimpleCertificate()); } + virtual std::vector<Certificate::ref> getPeerCertificateChain() const { + return std::vector<Certificate::ref>(); + } + virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const { return boost::shared_ptr<CertificateVerificationError>(); } virtual bool supportsZLibCompression() { return true; } virtual void addZLibCompression() { diff --git a/Swiften/Component/ComponentSessionStanzaChannel.h b/Swiften/Component/ComponentSessionStanzaChannel.h index 45f90b5..4e133b8 100644 --- a/Swiften/Component/ComponentSessionStanzaChannel.h +++ b/Swiften/Component/ComponentSessionStanzaChannel.h @@ -25,18 +25,23 @@ namespace Swift { void sendIQ(boost::shared_ptr<IQ> iq); void sendMessage(boost::shared_ptr<Message> message); void sendPresence(boost::shared_ptr<Presence> presence); bool getStreamManagementEnabled() const { return false; } + std::vector<Certificate::ref> getPeerCertificateChain() const { + // TODO: actually implement this method + return std::vector<Certificate::ref>(); + } + bool isAvailable() const { return session && session->getState() == ComponentSession::Initialized; } private: std::string getNewIQID(); void send(boost::shared_ptr<Stanza> stanza); void handleSessionFinished(boost::shared_ptr<Error> error); void handleStanza(boost::shared_ptr<Stanza> stanza); diff --git a/Swiften/Component/UnitTest/ComponentSessionTest.cpp b/Swiften/Component/UnitTest/ComponentSessionTest.cpp index da9ca7d..238d0b0 100644 --- a/Swiften/Component/UnitTest/ComponentSessionTest.cpp +++ b/Swiften/Component/UnitTest/ComponentSessionTest.cpp @@ -132,18 +132,22 @@ class ComponentSessionTest : public CppUnit::TestFixture { virtual ByteArray getTLSFinishMessage() const { return ByteArray(); } virtual Certificate::ref getPeerCertificate() const { return Certificate::ref(); } + virtual std::vector<Certificate::ref> getPeerCertificateChain() const { + return std::vector<Certificate::ref>(); + } + virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const { return boost::shared_ptr<CertificateVerificationError>(); } virtual bool supportsZLibCompression() { return true; } virtual void addZLibCompression() { diff --git a/Swiften/Session/BOSHSessionStream.cpp b/Swiften/Session/BOSHSessionStream.cpp index 237a394..6f15b84 100644 --- a/Swiften/Session/BOSHSessionStream.cpp +++ b/Swiften/Session/BOSHSessionStream.cpp @@ -123,18 +123,22 @@ void BOSHSessionStream::addTLSEncryption() { bool BOSHSessionStream::isTLSEncrypted() { return false; } Certificate::ref BOSHSessionStream::getPeerCertificate() const { return Certificate::ref(); } +std::vector<Certificate::ref> BOSHSessionStream::getPeerCertificateChain() const { + return std::vector<Certificate::ref>(); +} + boost::shared_ptr<CertificateVerificationError> BOSHSessionStream::getPeerCertificateVerificationError() const { return boost::shared_ptr<CertificateVerificationError>(); } ByteArray BOSHSessionStream::getTLSFinishMessage() const { return ByteArray(); } bool BOSHSessionStream::supportsZLibCompression() { diff --git a/Swiften/Session/BOSHSessionStream.h b/Swiften/Session/BOSHSessionStream.h index 497d391..99851b5 100644 --- a/Swiften/Session/BOSHSessionStream.h +++ b/Swiften/Session/BOSHSessionStream.h @@ -55,18 +55,19 @@ namespace Swift { virtual void writeData(const std::string& data); virtual bool supportsZLibCompression(); virtual void addZLibCompression(); virtual bool supportsTLSEncryption(); virtual void addTLSEncryption(); virtual bool isTLSEncrypted(); virtual Certificate::ref getPeerCertificate() const; + virtual std::vector<Certificate::ref> getPeerCertificateChain() const; virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; virtual ByteArray getTLSFinishMessage() const; virtual void setWhitespacePingEnabled(bool); virtual void resetXMPPParser(); private: void handleXMPPError(); diff --git a/Swiften/Session/BasicSessionStream.cpp b/Swiften/Session/BasicSessionStream.cpp index b49ffc9..cbe85ab 100644 --- a/Swiften/Session/BasicSessionStream.cpp +++ b/Swiften/Session/BasicSessionStream.cpp @@ -123,18 +123,22 @@ void BasicSessionStream::addTLSEncryption() { bool BasicSessionStream::isTLSEncrypted() { return tlsLayer; } Certificate::ref BasicSessionStream::getPeerCertificate() const { return tlsLayer->getPeerCertificate(); } +std::vector<Certificate::ref> BasicSessionStream::getPeerCertificateChain() const { + return tlsLayer->getPeerCertificateChain(); +} + boost::shared_ptr<CertificateVerificationError> BasicSessionStream::getPeerCertificateVerificationError() const { return tlsLayer->getPeerCertificateVerificationError(); } ByteArray BasicSessionStream::getTLSFinishMessage() const { return tlsLayer->getContext()->getFinishMessage(); } bool BasicSessionStream::supportsZLibCompression() { diff --git a/Swiften/Session/BasicSessionStream.h b/Swiften/Session/BasicSessionStream.h index e1f32f4..084a191 100644 --- a/Swiften/Session/BasicSessionStream.h +++ b/Swiften/Session/BasicSessionStream.h @@ -49,18 +49,20 @@ namespace Swift { virtual void writeData(const std::string& data); virtual bool supportsZLibCompression(); virtual void addZLibCompression(); virtual bool supportsTLSEncryption(); virtual void addTLSEncryption(); virtual bool isTLSEncrypted(); virtual Certificate::ref getPeerCertificate() const; + virtual std::vector<Certificate::ref> getPeerCertificateChain() const; + virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; virtual ByteArray getTLSFinishMessage() const; virtual void setWhitespacePingEnabled(bool); virtual void resetXMPPParser(); private: void handleConnectionFinished(const boost::optional<Connection::Error>& error); diff --git a/Swiften/Session/SessionStream.h b/Swiften/Session/SessionStream.h index 32cb6b6..4735d5f 100644 --- a/Swiften/Session/SessionStream.h +++ b/Swiften/Session/SessionStream.h @@ -61,18 +61,19 @@ namespace Swift { void setTLSCertificate(CertificateWithKey::ref cert) { certificate = cert; } virtual bool hasTLSCertificate() { return certificate && !certificate->isNull(); } virtual Certificate::ref getPeerCertificate() const = 0; + virtual std::vector<Certificate::ref> getPeerCertificateChain() 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; diff --git a/Swiften/StreamStack/TLSLayer.cpp b/Swiften/StreamStack/TLSLayer.cpp index 6d416bc..86221ea 100644 --- a/Swiften/StreamStack/TLSLayer.cpp +++ b/Swiften/StreamStack/TLSLayer.cpp @@ -39,14 +39,18 @@ void TLSLayer::handleDataRead(const SafeByteArray& data) { bool TLSLayer::setClientCertificate(CertificateWithKey::ref certificate) { return context->setClientCertificate(certificate); } Certificate::ref TLSLayer::getPeerCertificate() const { return context->getPeerCertificate(); } +std::vector<Certificate::ref> TLSLayer::getPeerCertificateChain() const { + return context->getPeerCertificateChain(); +} + boost::shared_ptr<CertificateVerificationError> TLSLayer::getPeerCertificateVerificationError() const { return context->getPeerCertificateVerificationError(); } } diff --git a/Swiften/StreamStack/TLSLayer.h b/Swiften/StreamStack/TLSLayer.h index ce0c89b..24978e0 100644 --- a/Swiften/StreamStack/TLSLayer.h +++ b/Swiften/StreamStack/TLSLayer.h @@ -20,18 +20,19 @@ namespace Swift { class TLSLayer : public StreamLayer { public: TLSLayer(TLSContextFactory*); ~TLSLayer(); void connect(); bool setClientCertificate(CertificateWithKey::ref cert); Certificate::ref getPeerCertificate() const; + std::vector<Certificate::ref> getPeerCertificateChain() const; boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; void writeData(const SafeByteArray& data); void handleDataRead(const SafeByteArray& data); TLSContext* getContext() const { return context; } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp index 8c03052..58a8d05 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp @@ -33,18 +33,24 @@ static const int SSL_READ_BUFFERSIZE = 8192; void freeX509Stack(STACK_OF(X509)* 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()); + // 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 // 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) { @@ -230,18 +236,30 @@ 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(); } } +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; +} + 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>(); } } diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h index d8d0d2f..cee4f79 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLContext.h +++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h @@ -22,18 +22,19 @@ namespace Swift { ~OpenSSLContext(); void connect(); bool setClientCertificate(CertificateWithKey::ref cert); void handleDataFromNetwork(const SafeByteArray&); void handleDataFromApplication(const SafeByteArray&); Certificate::ref getPeerCertificate() const; + std::vector<Certificate::ref> getPeerCertificateChain() const; boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; virtual ByteArray getFinishMessage() const; private: static void ensureLibraryInitialized(); static CertificateVerificationError::Type getVerificationErrorTypeForResult(int); diff --git a/Swiften/TLS/Schannel/SchannelContext.cpp b/Swiften/TLS/Schannel/SchannelContext.cpp index 641568d..997d760 100644 --- a/Swiften/TLS/Schannel/SchannelContext.cpp +++ b/Swiften/TLS/Schannel/SchannelContext.cpp @@ -627,18 +627,47 @@ void SchannelContext::handleCertificateCardRemoved() { Certificate::ref SchannelContext::getPeerCertificate() const { ScopedCertContext pServerCert; SECURITY_STATUS status = QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset()); return status == SEC_E_OK ? boost::make_shared<SchannelCertificate>(pServerCert) : SchannelCertificate::ref(); } //------------------------------------------------------------------------ +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; +} + +//------------------------------------------------------------------------ + CertificateVerificationError::ref SchannelContext::getPeerCertificateVerificationError() const { return m_verificationError ? boost::make_shared<CertificateVerificationError>(*m_verificationError) : CertificateVerificationError::ref(); } //------------------------------------------------------------------------ ByteArray SchannelContext::getFinishMessage() const { SecPkgContext_Bindings bindings; int ret = QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_UNIQUE_BINDINGS, &bindings); diff --git a/Swiften/TLS/Schannel/SchannelContext.h b/Swiften/TLS/Schannel/SchannelContext.h index 587d0e7..2d65a8a 100644 --- a/Swiften/TLS/Schannel/SchannelContext.h +++ b/Swiften/TLS/Schannel/SchannelContext.h @@ -45,18 +45,19 @@ namespace Swift // TLSContext // virtual void connect(); virtual bool setClientCertificate(CertificateWithKey::ref cert); virtual void handleDataFromNetwork(const SafeByteArray& data); virtual void handleDataFromApplication(const SafeByteArray& data); virtual Certificate::ref getPeerCertificate() const; + virtual std::vector<Certificate::ref> getPeerCertificateChain() const; virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const; virtual ByteArray getFinishMessage() const; virtual void setCheckCertificateRevocation(bool b); private: void determineStreamSizes(); void continueHandshake(const SafeByteArray& data); diff --git a/Swiften/TLS/TLSContext.h b/Swiften/TLS/TLSContext.h index 5640fe1..388f8ee 100644 --- a/Swiften/TLS/TLSContext.h +++ b/Swiften/TLS/TLSContext.h @@ -23,18 +23,19 @@ namespace Swift { virtual void connect() = 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 std::vector<Certificate::ref> getPeerCertificateChain() 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 (boost::shared_ptr<TLSError>)> onError; boost::signal<void ()> onConnected; |
Swift