From f32492bec456866fb5101274e6789efc59a47bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Sun, 5 Dec 2010 19:06:41 +0100 Subject: Added plumbing for persistent certificate trust checking. diff --git a/.project b/.project index 0b4af0a..fcdfcdc 100644 --- a/.project +++ b/.project @@ -18,7 +18,7 @@ org.eclipse.cdt.make.core.autoBuildTarget - doc=1 Documentation/SwiftenDevelopersGuide + org.eclipse.cdt.make.core.buildArguments @@ -29,10 +29,6 @@ python - org.eclipse.cdt.make.core.buildLocation - - - org.eclipse.cdt.make.core.cleanBuildTarget -c @@ -54,7 +50,7 @@ org.eclipse.cdt.make.core.fullBuildTarget - doc=1 Documentation/SwiftenDevelopersGuide + org.eclipse.cdt.make.core.stopOnError diff --git a/BuildTools/Eclipse/Swift (Mac OS X).launch b/BuildTools/Eclipse/Swift (Mac OS X).launch index e515972..1dc569f 100644 --- a/BuildTools/Eclipse/Swift (Mac OS X).launch +++ b/BuildTools/Eclipse/Swift (Mac OS X).launch @@ -17,7 +17,7 @@ - + diff --git a/Swift/Controllers/CertificateFileStorage.cpp b/Swift/Controllers/CertificateFileStorage.cpp new file mode 100644 index 0000000..65da1ec --- /dev/null +++ b/Swift/Controllers/CertificateFileStorage.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +namespace Swift { + +CertificateFileStorage::CertificateFileStorage(const boost::filesystem::path& path, CertificateFactory* certificateFactory) : path(path), certificateFactory(certificateFactory) { +} + +bool CertificateFileStorage::hasCertificate(Certificate::ref certificate) const { + boost::filesystem::path certificatePath = getCertificatePath(certificate); + if (boost::filesystem::exists(certificatePath)) { + ByteArray data; + data.readFromFile(certificatePath.string()); + Certificate::ref storedCertificate = certificateFactory->createCertificateFromDER(data); + if (storedCertificate && storedCertificate->toDER() == certificate->toDER()) { + return true; + } + else { + SWIFT_LOG(warning) << "Stored certificate does not match received certificate" << std::endl; + return false; + } + } + else { + return false; + } +} + +void CertificateFileStorage::addCertificate(Certificate::ref certificate) { + boost::filesystem::path certificatePath = getCertificatePath(certificate); + if (!boost::filesystem::exists(certificatePath.parent_path())) { + try { + boost::filesystem::create_directories(certificatePath.parent_path()); + } + catch (const boost::filesystem::filesystem_error& e) { + std::cerr << "ERROR: " << e.what() << std::endl; + } + } + boost::filesystem::ofstream file(certificatePath, boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out); + ByteArray data = certificate->toDER(); + file.write(data.getData(), data.getSize()); + file.close(); +} + +boost::filesystem::path CertificateFileStorage::getCertificatePath(Certificate::ref certificate) const { + return path / Hexify::hexify(SHA1::getHash(certificate->toDER())).getUTF8String(); +} + +} diff --git a/Swift/Controllers/CertificateFileStorage.h b/Swift/Controllers/CertificateFileStorage.h new file mode 100644 index 0000000..2b853ed --- /dev/null +++ b/Swift/Controllers/CertificateFileStorage.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include + +#include "Swift/Controllers/CertificateStorage.h" + +namespace Swift { + class CertificateFactory; + + class CertificateFileStorage : public CertificateStorage { + public: + CertificateFileStorage(const boost::filesystem::path& path, CertificateFactory* certificateFactory); + + virtual bool hasCertificate(Certificate::ref certificate) const; + virtual void addCertificate(Certificate::ref certificate); + + private: + boost::filesystem::path getCertificatePath(Certificate::ref certificate) const; + + private: + boost::filesystem::path path; + CertificateFactory* certificateFactory; + }; + +} diff --git a/Swift/Controllers/CertificateFileStorageFactory.h b/Swift/Controllers/CertificateFileStorageFactory.h new file mode 100644 index 0000000..bcac56d --- /dev/null +++ b/Swift/Controllers/CertificateFileStorageFactory.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class CertificateFactory; + + class CertificateFileStorageFactory : public CertificateStorageFactory { + public: + CertificateFileStorageFactory(const boost::filesystem::path& basePath, CertificateFactory* certificateFactory) : basePath(basePath), certificateFactory(certificateFactory) {} + + virtual CertificateStorage* createCertificateStorage(const JID& profile) const { + boost::filesystem::path profilePath = basePath / profile.toString().getUTF8String(); + return new CertificateFileStorage(profilePath / "certificates", certificateFactory); + } + + private: + boost::filesystem::path basePath; + CertificateFactory* certificateFactory; + }; +} diff --git a/Swift/Controllers/CertificateStorage.cpp b/Swift/Controllers/CertificateStorage.cpp new file mode 100644 index 0000000..343fccd --- /dev/null +++ b/Swift/Controllers/CertificateStorage.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swift/Controllers/CertificateStorage.h" + +namespace Swift { + +CertificateStorage::~CertificateStorage() { +} + +} diff --git a/Swift/Controllers/CertificateStorage.h b/Swift/Controllers/CertificateStorage.h new file mode 100644 index 0000000..f8c6fb5 --- /dev/null +++ b/Swift/Controllers/CertificateStorage.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include + +#include + +namespace Swift { + class CertificateStorage { + public: + virtual ~CertificateStorage(); + + virtual bool hasCertificate(Certificate::ref certificate) const = 0; + virtual void addCertificate(Certificate::ref certificate) = 0; + }; + +} diff --git a/Swift/Controllers/CertificateStorageFactory.cpp b/Swift/Controllers/CertificateStorageFactory.cpp new file mode 100644 index 0000000..613a8c3 --- /dev/null +++ b/Swift/Controllers/CertificateStorageFactory.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include + +namespace Swift { + +CertificateStorageFactory::~CertificateStorageFactory() { +} + +} diff --git a/Swift/Controllers/CertificateStorageFactory.h b/Swift/Controllers/CertificateStorageFactory.h new file mode 100644 index 0000000..5b85757 --- /dev/null +++ b/Swift/Controllers/CertificateStorageFactory.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +namespace Swift { + class CertificateStorage; + class JID; + + class CertificateStorageFactory { + public: + virtual ~CertificateStorageFactory(); + + virtual CertificateStorage* createCertificateStorage(const JID& profile) const = 0; + }; +} diff --git a/Swift/Controllers/CertificateStorageTrustChecker.h b/Swift/Controllers/CertificateStorageTrustChecker.h new file mode 100644 index 0000000..f33287c --- /dev/null +++ b/Swift/Controllers/CertificateStorageTrustChecker.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include +#include + +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); + } + + Certificate::ref getLastCertificate() const { + return lastCertificate; + } + + private: + CertificateStorage* storage; + Certificate::ref lastCertificate; + }; +} diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 8d78671..878ed9b 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -55,6 +55,8 @@ #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" #include "Swift/Controllers/UIEvents/ToggleNotificationsUIEvent.h" #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" +#include "Swift/Controllers/CertificateStorageFactory.h" +#include "Swift/Controllers/CertificateStorageTrustChecker.h" namespace Swift { @@ -76,6 +78,7 @@ MainController::MainController( ChatListWindowFactory* chatListWindowFactory, MUCSearchWindowFactory* mucSearchWindowFactory, StoragesFactory* storagesFactory, + CertificateStorageFactory* certificateStorageFactory, Dock* dock, Notifier* notifier, bool useDelayForLatency) : @@ -83,6 +86,7 @@ MainController::MainController( networkFactories_(eventLoop), idleDetector_(&idleQuerier_, networkFactories_.getTimerFactory(), 100), storagesFactory_(storagesFactory), + certificateStorageFactory_(certificateStorageFactory), chatWindowFactory_(chatWindowFactory), mainWindowFactory_(mainWindowFactory), loginWindowFactory_(loginWindowFactory), @@ -90,6 +94,7 @@ MainController::MainController( loginWindow_(NULL) , useDelayForLatency_(useDelayForLatency) { storages_ = NULL; + certificateStorage_ = NULL; statusTracker_ = NULL; client_ = NULL; presenceNotifier_ = NULL; @@ -182,6 +187,8 @@ void MainController::resetClient() { presenceNotifier_ = NULL; delete client_; client_ = NULL; + delete certificateStorage_; + certificateStorage_ = NULL; delete storages_; storages_ = NULL; delete statusTracker_; @@ -353,7 +360,11 @@ void MainController::performLoginFromCachedCredentials() { } if (!client_) { storages_ = storagesFactory_->createStorages(jid_); + certificateStorage_ = certificateStorageFactory_->createCertificateStorage(jid_); + certificateTrustChecker_ = new CertificateStorageTrustChecker(certificateStorage_); client_ = new Swift::Client(eventLoop_, &networkFactories_, jid_, password_, storages_); + client_->setCertificateTrustChecker(certificateTrustChecker_); + // FIXME: Remove this line to activate the trust checker client_->setAlwaysTrustCertificates(); client_->onDataRead.connect(boost::bind(&XMLConsoleController::handleDataRead, xmlConsoleController_, _1)); client_->onDataWritten.connect(boost::bind(&XMLConsoleController::handleDataWritten, xmlConsoleController_, _1)); @@ -427,8 +438,11 @@ void MainController::handleDisconnected(const boost::optional& erro case ClientError::InvalidCertificateSignatureError: case ClientError::InvalidCAError: case ClientError::InvalidServerIdentityError: - // TODO - message = "Certificate error"; break; + // FIXME: Popup a dialog + message = "Certificate error"; + // FIXME: Only do this if the user accepts the certificate + //certificateStorage_->addCertificate(certificateTrustChecker_->getLastCertificate()); + break; } if (!rosterController_) { //hasn't been logged in yet signOut(); diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index 8f7298b..1256345 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -33,6 +33,9 @@ namespace Swift { class ChatWindowFactory; class ChatController; class ChatsManager; + class CertificateStorageFactory; + class CertificateStorage; + class CertificateStorageTrustChecker; class ChatListWindowFactory; class EventController; class MainWindowFactory; @@ -77,6 +80,7 @@ namespace Swift { ChatListWindowFactory* chatListWindowFactory_, MUCSearchWindowFactory* mucSearchWindowFactory, StoragesFactory* storagesFactory, + CertificateStorageFactory* certificateStorageFactory, Dock* dock, Notifier* notifier, bool useDelayForLatency); @@ -115,6 +119,9 @@ namespace Swift { ActualIdleDetector idleDetector_; StoragesFactory* storagesFactory_; Storages* storages_; + CertificateStorageFactory* certificateStorageFactory_; + CertificateStorage* certificateStorage_; + CertificateStorageTrustChecker* certificateTrustChecker_; Client* client_; ChatWindowFactory* chatWindowFactory_; MainWindowFactory* mainWindowFactory_; diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index d8b2781..9cd2be4 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -41,6 +41,9 @@ if env["SCONS_STAGE"] == "build" : "UIInterfaces/XMLConsoleWidget.cpp", "UIInterfaces/ChatListWindow.cpp", "PreviousStatusStore.cpp", + "CertificateStorageFactory.cpp", + "CertificateStorage.cpp", + "CertificateFileStorage.cpp", ]) env.Append(UNITTEST_SOURCES = [ diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index f33706a..2890357 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -24,6 +24,7 @@ #include #include +#include #include "SwifTools/Application/PlatformApplicationPathProvider.h" #include "Swiften/Avatars/AvatarFileStorage.h" #include "Swiften/Disco/CapsFileStorage.h" @@ -102,6 +103,7 @@ QtSwift::QtSwift(po::variables_map options) : autoUpdater_(NULL) { settings_ = new QtSettingsProvider(); applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME); storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir()); + certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory()); chatWindowFactory_ = new QtChatWindowFactory(splitter_, settings_, tabs_, ""); soundPlayer_ = new QtSoundPlayer(applicationPathProvider_); #if defined(HAVE_GROWL) @@ -153,6 +155,7 @@ QtSwift::QtSwift(po::variables_map options) : autoUpdater_(NULL) { chatListWindowFactory, mucSearchWindowFactory, storagesFactory_, + certificateStorageFactory_, dock_, notifier_, options.count("latency-debug") > 0); @@ -200,6 +203,7 @@ QtSwift::~QtSwift() { foreach (QtChatListWindowFactory* factory, chatListWindowFactories_) { delete factory; } + delete certificateStorageFactory_; delete storagesFactory_; } diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h index 6817734..a41e2d1 100644 --- a/Swift/QtUI/QtSwift.h +++ b/Swift/QtUI/QtSwift.h @@ -9,6 +9,7 @@ #include #include +#include #include "Swiften/Base/String.h" #include "Swiften/Base/Platform.h" #include "Swiften/EventLoop/Qt/QtEventLoop.h" @@ -28,6 +29,7 @@ namespace po = boost::program_options; class QSplitter; namespace Swift { + class CertificateStorageFactory; class Dock; class Notifier; class StoragesFactory; @@ -54,6 +56,7 @@ namespace Swift { static po::options_description getOptionsDescription(); ~QtSwift(); private: + PlatformTLSFactories tlsFactories_; std::vector mainControllers_; QtChatWindowFactory *chatWindowFactory_; std::vector rosterWindowFactories_; @@ -71,6 +74,7 @@ namespace Swift { QtChatTabs* tabs_; ApplicationPathProvider* applicationPathProvider_; StoragesFactory* storagesFactory_; + CertificateStorageFactory* certificateStorageFactory_; AutoUpdater* autoUpdater_; Notifier* notifier_; #if defined(SWIFTEN_PLATFORM_MACOSX) diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp index 9e6db5d..13816d3 100644 --- a/Swiften/Client/ClientSession.cpp +++ b/Swiften/Client/ClientSession.cpp @@ -346,7 +346,7 @@ void ClientSession::handleTLSEncrypted() { } void ClientSession::checkTrustOrFinish(Certificate::ref certificate, boost::shared_ptr error) { - if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificate, localJID.getDomain())) { + if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificate)) { continueAfterTLSEncrypted(); } else { diff --git a/Swiften/TLS/BlindCertificateTrustChecker.h b/Swiften/TLS/BlindCertificateTrustChecker.h index fc7fbe8..d9db14c 100644 --- a/Swiften/TLS/BlindCertificateTrustChecker.h +++ b/Swiften/TLS/BlindCertificateTrustChecker.h @@ -19,7 +19,7 @@ namespace Swift { */ class BlindCertificateTrustChecker : public CertificateTrustChecker { public: - virtual bool isCertificateTrusted(Certificate::ref, const String&) { + virtual bool isCertificateTrusted(Certificate::ref) { return true; } }; diff --git a/Swiften/TLS/CertificateTrustChecker.h b/Swiften/TLS/CertificateTrustChecker.h index 7400dac..c248e4a 100644 --- a/Swiften/TLS/CertificateTrustChecker.h +++ b/Swiften/TLS/CertificateTrustChecker.h @@ -24,6 +24,6 @@ namespace Swift { * trusted. This usually happens when a certificate's validation * fails, to check whether to proceed with the connection or not. */ - virtual bool isCertificateTrusted(Certificate::ref certificate, const String& domain) = 0; + virtual bool isCertificateTrusted(Certificate::ref certificate) = 0; }; } diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp index 347d6ef..6a3d688 100644 --- a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp +++ b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp @@ -4,9 +4,10 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/TLS/OpenSSL/OpenSSLCertificate.h" +#include -#include "Swiften/Base/ByteArray.h" +#include +#include #undef X509_NAME // Windows.h defines this, and for some reason, it doesn't get undeffed properly in x509.h #include @@ -27,6 +28,9 @@ OpenSSLCertificate::OpenSSLCertificate(const ByteArray& der) { const unsigned char* p = reinterpret_cast(der.getData()); #endif cert = boost::shared_ptr(d2i_X509(NULL, &p, der.getSize()), X509_free); + if (!cert) { + SWIFT_LOG(warning) << "Error creating certificate from DER data" << std::endl; + } parse(); } -- cgit v0.10.2-6-g49f6