diff options
| -rw-r--r-- | Swift/Controllers/MainController.cpp | 6 | ||||
| -rw-r--r-- | Swift/Controllers/Storages/CertificateStorageTrustChecker.h | 12 | ||||
| -rw-r--r-- | Swift/Controllers/UIInterfaces/LoginWindow.h | 2 | ||||
| -rw-r--r-- | Swift/QtUI/QtLoginWindow.cpp | 21 | ||||
| -rw-r--r-- | Swift/QtUI/QtLoginWindow.h | 2 | ||||
| -rw-r--r-- | Swift/QtUI/QtMainWindow.cpp | 8 | ||||
| -rw-r--r-- | Swift/QtUI/QtMainWindow.h | 1 | ||||
| -rw-r--r-- | Swiften/Client/ClientSession.cpp | 9 | ||||
| -rw-r--r-- | Swiften/Client/ClientSession.h | 2 | ||||
| -rw-r--r-- | Swiften/TLS/BlindCertificateTrustChecker.h | 2 | ||||
| -rw-r--r-- | Swiften/TLS/CertificateTrustChecker.h | 6 |
11 files changed, 43 insertions, 28 deletions
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 40b4ded..cb43057 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -553,21 +553,21 @@ void MainController::handleDisconnected(const boost::optional<ClientError>& erro case ClientError::CertificatePathLengthExceededError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate path length constraint exceeded"); break; case ClientError::InvalidCertificateSignatureError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Invalid certificate signature"); break; case ClientError::InvalidCAError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Invalid Certificate Authority"); break; case ClientError::InvalidServerIdentityError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate does not match the host identity"); break; case ClientError::RevokedError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate has been revoked"); break; case ClientError::RevocationCheckFailedError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Unable to determine certificate revocation state"); break; } bool forceReconnectAfterCertificateTrust = false; if (!certificateErrorMessage.empty()) { - Certificate::ref certificate = certificateTrustChecker_->getLastCertificate(); - if (loginWindow_->askUserToTrustCertificatePermanently(certificateErrorMessage, certificate)) { - certificateStorage_->addCertificate(certificate); + std::vector<Certificate::ref> certificates = certificateTrustChecker_->getLastCertificateChain(); + if (!certificates.empty() && loginWindow_->askUserToTrustCertificatePermanently(certificateErrorMessage, certificates)) { + certificateStorage_->addCertificate(certificates[0]); forceReconnectAfterCertificateTrust = true; } else { message = QT_TRANSLATE_NOOP("", "Certificate error"); } } if (forceReconnectAfterCertificateTrust && settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { forceReconnectAfterCertificateTrust = false; diff --git a/Swift/Controllers/Storages/CertificateStorageTrustChecker.h b/Swift/Controllers/Storages/CertificateStorageTrustChecker.h index 40838dd..a73590a 100644 --- a/Swift/Controllers/Storages/CertificateStorageTrustChecker.h +++ b/Swift/Controllers/Storages/CertificateStorageTrustChecker.h @@ -12,23 +12,23 @@ namespace Swift { /** * A certificate trust checker that trusts certificates in a certificate storage. */ class CertificateStorageTrustChecker : public CertificateTrustChecker { public: CertificateStorageTrustChecker(CertificateStorage* storage) : storage(storage) { } - virtual bool isCertificateTrusted(Certificate::ref certificate) { - lastCertificate = certificate; - return storage->hasCertificate(certificate); + virtual bool isCertificateTrusted(Certificate::ref, const std::vector<Certificate::ref>& certificateChain) { + lastCertificateChain = std::vector<Certificate::ref>(certificateChain.begin(), certificateChain.end()); + return certificateChain.empty() ? false : storage->hasCertificate(certificateChain[0]); } - Certificate::ref getLastCertificate() const { - return lastCertificate; + const std::vector<Certificate::ref>& getLastCertificateChain() const { + return lastCertificateChain; } private: CertificateStorage* storage; - Certificate::ref lastCertificate; + std::vector<Certificate::ref> lastCertificateChain; }; } diff --git a/Swift/Controllers/UIInterfaces/LoginWindow.h b/Swift/Controllers/UIInterfaces/LoginWindow.h index bbbbe35..f1c2ce1 100644 --- a/Swift/Controllers/UIInterfaces/LoginWindow.h +++ b/Swift/Controllers/UIInterfaces/LoginWindow.h @@ -25,16 +25,16 @@ namespace Swift { virtual void setMessage(const std::string&) = 0; 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; /** The certificate is what is used for login, the certificatePath is used for remembering paths to populate the loginwindow with*/ boost::signal<void (const std::string&, const std::string&, const std::string& /*CertificatePath*/, CertificateWithKey::ref /* clientCertificate */, bool /* remember password*/, bool /* login automatically */)> onLoginRequest; virtual void setLoginAutomatically(bool loginAutomatically) = 0; virtual void quit() = 0; /** Blocking request whether a cert should be permanently trusted.*/ - virtual bool askUserToTrustCertificatePermanently(const std::string& message, Certificate::ref) = 0; + virtual bool askUserToTrustCertificatePermanently(const std::string& message, const std::vector<Certificate::ref>& certificateChain) = 0; boost::signal<void ()> onCancelLoginRequest; boost::signal<void ()> onQuitRequest; boost::signal<void (const std::string&)> onPurgeSavedLoginRequest; }; } diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp index 094f96c..42ebe39 100644 --- a/Swift/QtUI/QtLoginWindow.cpp +++ b/Swift/QtUI/QtLoginWindow.cpp @@ -500,26 +500,31 @@ QtLoginWindow::QtMenus QtLoginWindow::getMenus() const { void QtLoginWindow::resizeEvent(QResizeEvent*) { emit geometryChanged(); } void QtLoginWindow::moveEvent(QMoveEvent*) { emit geometryChanged(); } -bool QtLoginWindow::askUserToTrustCertificatePermanently(const std::string& message, Certificate::ref certificate) { +bool QtLoginWindow::askUserToTrustCertificatePermanently(const std::string& message, const std::vector<Certificate::ref>& certificates) { QMessageBox dialog(this); dialog.setText(tr("The certificate presented by the server is not valid.")); dialog.setInformativeText(P2QSTRING(message) + "\n\n" + tr("Would you like to permanently trust this certificate? This must only be done if you know it is correct.")); - QString detailedText = tr("Subject: %1").arg(P2QSTRING(certificate->getSubjectName())) + "\n"; - detailedText += tr("SHA-1 Fingerprint: %1").arg(P2QSTRING(certificate->getSHA1Fingerprint())); - dialog.setDetailedText(detailedText); - - dialog.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + dialog.addButton("Show Certificate", QMessageBox::HelpRole); + dialog.addButton(QMessageBox::Yes); + dialog.addButton(QMessageBox::No); dialog.setDefaultButton(QMessageBox::No); - - return dialog.exec() == QMessageBox::Yes; + while (true) { + int result = dialog.exec(); + if (result == QMessageBox::Yes || result == QMessageBox::No) { + return result == QMessageBox::Yes; + } + // FIXME: This isn't very nice, because the dialog disappears every time. We actually need a real + // dialog with a custom button. + QtMainWindow::openCertificateDialog(certificates, &dialog); + } } } diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h index 8f50a35..572902e 100644 --- a/Swift/QtUI/QtLoginWindow.h +++ b/Swift/QtUI/QtLoginWindow.h @@ -42,19 +42,19 @@ namespace Swift { void morphInto(MainWindow *mainWindow); virtual void loggedOut(); virtual void setShowNotificationToggle(bool); virtual void setMessage(const std::string& message); virtual void addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate); virtual void removeAvailableAccount(const std::string& jid); virtual void setLoginAutomatically(bool loginAutomatically); virtual void setIsLoggingIn(bool loggingIn); void selectUser(const std::string& user); - bool askUserToTrustCertificatePermanently(const std::string& message, Certificate::ref certificate); + bool askUserToTrustCertificatePermanently(const std::string& message, const std::vector<Certificate::ref>& certificate); void hide(); QtMenus getMenus() const; virtual void quit(); signals: void geometryChanged(); private slots: void loginClicked(); diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 547f22b..ec05684 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -267,22 +267,26 @@ void QtMainWindow::setMyStatusType(StatusShow::Type type) { void QtMainWindow::setConnecting() { meView_->setConnecting(); } void QtMainWindow::setStreamEncryptionStatus(bool tlsInPlaceAndValid) { meView_->setStreamEncryptionStatus(tlsInPlaceAndValid); } void QtMainWindow::openCertificateDialog(const std::vector<Certificate::ref>& chain) { + openCertificateDialog(chain, this); +} + +void QtMainWindow::openCertificateDialog(const std::vector<Certificate::ref>& chain, QWidget* parent) { #if defined(SWIFTEN_PLATFORM_MACOSX) - CocoaUIHelpers::displayCertificateChainAsSheet(this, chain); + CocoaUIHelpers::displayCertificateChainAsSheet(parent, chain); #elif defined(SWIFTEN_PLATFORM_WINDOWS) - WinUIHelpers::displayCertificateChainAsSheet(this,chain); + WinUIHelpers::displayCertificateChainAsSheet(parent, 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)]; diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h index c725d08..25d9ace 100644 --- a/Swift/QtUI/QtMainWindow.h +++ b/Swift/QtUI/QtMainWindow.h @@ -41,18 +41,19 @@ namespace Swift { 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); + static void openCertificateDialog(const std::vector<Certificate::ref>& chain, QWidget* parent); 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(); diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp index 8be8a8c..c2dc3ae 100644 --- a/Swiften/Client/ClientSession.cpp +++ b/Swiften/Client/ClientSession.cpp @@ -365,35 +365,36 @@ void ClientSession::sendCredentials(const SafeByteArray& password) { state = Authenticating; authenticator->setCredentials(localJID.getNode(), password); stream->writeElement(boost::make_shared<AuthRequest>(authenticator->getName(), authenticator->getResponse())); } void ClientSession::handleTLSEncrypted() { checkState(Encrypting); Certificate::ref certificate = stream->getPeerCertificate(); + std::vector<Certificate::ref> certificateChain = stream->getPeerCertificateChain(); boost::shared_ptr<CertificateVerificationError> verificationError = stream->getPeerCertificateVerificationError(); if (verificationError) { - checkTrustOrFinish(certificate, verificationError); + checkTrustOrFinish(certificate, certificateChain, verificationError); } else { ServerIdentityVerifier identityVerifier(localJID); if (identityVerifier.certificateVerifies(certificate)) { continueAfterTLSEncrypted(); } else { - checkTrustOrFinish(certificate, boost::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidServerIdentity)); + checkTrustOrFinish(certificate, certificateChain, boost::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidServerIdentity)); } } } -void ClientSession::checkTrustOrFinish(Certificate::ref certificate, boost::shared_ptr<CertificateVerificationError> error) { - if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificate)) { +void ClientSession::checkTrustOrFinish(Certificate::ref certificate, const std::vector<Certificate::ref>& certificateChain, boost::shared_ptr<CertificateVerificationError> error) { + if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificate, certificateChain)) { continueAfterTLSEncrypted(); } else { finishSession(error); } } void ClientSession::continueAfterTLSEncrypted() { state = WaitingForStreamStart; diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h index b67b23d..9c4b980 100644 --- a/Swiften/Client/ClientSession.h +++ b/Swiften/Client/ClientSession.h @@ -148,19 +148,19 @@ namespace Swift { void handleTLSEncrypted(); bool checkState(State); void continueSessionInitialization(); void requestAck(); void handleStanzaAcked(boost::shared_ptr<Stanza> stanza); void ack(unsigned int handledStanzasCount); void continueAfterTLSEncrypted(); - void checkTrustOrFinish(Certificate::ref certificate, boost::shared_ptr<CertificateVerificationError> error); + void checkTrustOrFinish(Certificate::ref certificate, const std::vector<Certificate::ref>& certificateChain, boost::shared_ptr<CertificateVerificationError> error); private: JID localJID; State state; boost::shared_ptr<SessionStream> stream; bool allowPLAINOverNonTLS; bool useStreamCompression; UseTLS useTLS; bool useAcks; diff --git a/Swiften/TLS/BlindCertificateTrustChecker.h b/Swiften/TLS/BlindCertificateTrustChecker.h index 3177322..9ed7ff2 100644 --- a/Swiften/TLS/BlindCertificateTrustChecker.h +++ b/Swiften/TLS/BlindCertificateTrustChecker.h @@ -13,14 +13,14 @@ namespace Swift { * A certificate trust checker that trusts any ceritficate. * * This can be used to ignore any TLS certificate errors occurring * during connection. * * \see Client::setAlwaysTrustCertificates() */ class BlindCertificateTrustChecker : public CertificateTrustChecker { public: - virtual bool isCertificateTrusted(Certificate::ref) { + virtual bool isCertificateTrusted(Certificate::ref, const std::vector<Certificate::ref>&) { return true; } }; } diff --git a/Swiften/TLS/CertificateTrustChecker.h b/Swiften/TLS/CertificateTrustChecker.h index 06c0c32..91cc530 100644 --- a/Swiften/TLS/CertificateTrustChecker.h +++ b/Swiften/TLS/CertificateTrustChecker.h @@ -4,26 +4,30 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <boost/shared_ptr.hpp> #include <string> #include <Swiften/TLS/Certificate.h> +#include <vector> namespace Swift { /** * A class to implement a check for certificate trust. */ class CertificateTrustChecker { public: virtual ~CertificateTrustChecker(); /** * This method is called to find out whether a certificate is * trusted. This usually happens when a certificate's validation * fails, to check whether to proceed with the connection or not. + * + * certificateChain contains the chain of certificates, if available. + * This chain includes certificate. */ - virtual bool isCertificateTrusted(Certificate::ref certificate) = 0; + virtual bool isCertificateTrusted(Certificate::ref certificate, const std::vector<Certificate::ref>& certificateChain) = 0; }; } |
Swift