From 1f8333f23f060750673d0b7c573f2e2d12142adf Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 30 Dec 2015 21:34:11 +0100 Subject: Support a trusted SHA1 fingerprint to be configured for each IRC server --- database/database.xml | 1 + louloulibs/network/credentials_manager.cpp | 15 ++++++++++++++- louloulibs/network/credentials_manager.hpp | 2 ++ louloulibs/network/tcp_socket_handler.hpp | 2 ++ src/irc/irc_client.cpp | 7 +++++++ src/xmpp/biboumi_adhoc_commands.cpp | 22 +++++++++++++++++++++- 6 files changed, 47 insertions(+), 2 deletions(-) diff --git a/database/database.xml b/database/database.xml index a22b49d..f102db0 100644 --- a/database/database.xml +++ b/database/database.xml @@ -13,6 +13,7 @@ + diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index 57100ee..f92aef8 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -26,11 +26,17 @@ bool Basic_Credentials_Manager::certs_loaded = false; Basic_Credentials_Manager::Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler): Botan::Credentials_Manager(), - socket_handler(socket_handler) + socket_handler(socket_handler), + trusted_fingerprint{} { this->load_certs(); } +void Basic_Credentials_Manager::set_trusted_fingerprint(const std::string& fingerprint) +{ + this->trusted_fingerprint = fingerprint; +} + void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector& certs) @@ -44,6 +50,13 @@ void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type catch (const std::exception& tls_exception) { log_warning("TLS certificate check failed: " << tls_exception.what()); + if (!this->trusted_fingerprint.empty() && !certs.empty() && + this->trusted_fingerprint == certs[0].fingerprint() && + certs[0].matches_dns_name(purported_hostname)) + // We trust the certificate, based on the trusted fingerprint and + // the fact that the hostname matches + return; + if (this->socket_handler->abort_on_invalid_cert()) throw; } diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index e292321..dbd1d95 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -19,6 +19,7 @@ public: const std::vector&) override final; std::vector trusted_certificate_authorities(const std::string& type, const std::string& context) override final; + void set_trusted_fingerprint(const std::string& fingerprint); private: const TCPSocketHandler* const socket_handler; @@ -26,6 +27,7 @@ private: static void load_certs(); static Botan::Certificate_Store_In_Memory certificate_store; static bool certs_loaded; + std::string trusted_fingerprint; }; #endif //BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 9f5caa3..fb4195d 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -248,7 +248,9 @@ private: static Botan::AutoSeeded_RNG rng; static Botan::TLS::Policy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; +protected: Basic_Credentials_Manager credential_manager; +private: /** * We use a unique_ptr because we may not want to create the object at * all. The Botan::TLS::Client object generates a handshake message and diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index e71d38c..1a83446 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -89,6 +89,13 @@ void IrcClient::start() this->bind_addr = Config::get("outgoing_bind", ""); +#ifdef BOTAN_FOUND +# ifdef USE_DATABASE + auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), + this->get_hostname()); + this->credential_manager.set_trusted_fingerprint(options.trustedFingerprint); +# endif +#endif this->connect(this->hostname, port, tls); } diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index be755e9..7c157cb 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -175,6 +175,19 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com verify_cert_value.set_inner("false"); verify_cert.add_child(std::move(verify_cert_value)); x.add_child(std::move(verify_cert)); + + XmlNode fingerprint("field"); + fingerprint["var"] = "fingerprint"; + fingerprint["type"] = "text-single"; + fingerprint["label"] = "SHA-1 fingerprint of the TLS certificate to trust."; + if (!options.trustedFingerprint.value().empty()) + { + XmlNode fingerprint_value("value"); + fingerprint_value.set_inner(options.trustedFingerprint.value()); + fingerprint.add_child(std::move(fingerprint_value)); + } + fingerprint.add_child(required); + x.add_child(std::move(fingerprint)); #endif XmlNode pass("field"); @@ -295,12 +308,19 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com options.tlsPorts = ports; } - else if (field->get_tag("var") == "verify_cert" && value + else if (field->get_tag("var") == "verify_cert" && value && !value->get_inner().empty()) { auto val = to_bool(value->get_inner()); options.verifyCert = val; } + + else if (field->get_tag("var") == "fingerprint" && value && + !value->get_inner().empty()) + { + options.trustedFingerprint = value->get_inner(); + } + #endif // BOTAN_FOUND else if (field->get_tag("var") == "pass" && -- cgit v1.2.3