From e8386bd14e9783f0bef39bdf577545522e33e719 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 3 Nov 2015 16:56:38 +0100 Subject: Provide an adhoc option to let user pass the cert verif for some IRC servers --- louloulibs/network/credentials_manager.cpp | 35 +++++++++++++++++++++++++----- louloulibs/network/credentials_manager.hpp | 11 +++++++--- louloulibs/network/tcp_socket_handler.cpp | 6 +++-- louloulibs/network/tcp_socket_handler.hpp | 12 +++++++++- src/irc/irc_client.cpp | 11 ++++++++++ src/irc/irc_client.hpp | 3 +++ src/xmpp/biboumi_adhoc_commands.cpp | 20 +++++++++++++++++ 7 files changed, 87 insertions(+), 11 deletions(-) diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index 7c13319..b9a9af8 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -1,25 +1,48 @@ #include "louloulibs.h" #ifdef BOTAN_FOUND +#include #include #include #include -Basic_Credentials_Manager::Basic_Credentials_Manager(): - Botan::Credentials_Manager() +#ifdef USE_DATABASE +# include +#endif + +Botan::Certificate_Store_In_Memory Basic_Credentials_Manager::certificate_store; +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) { this->load_certs(); } + void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector& certs) { log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); - Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs); - log_debug("Certificate is valid"); + try + { + Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs); + log_debug("Certificate is valid"); + } + catch (const std::exception& tls_exception) + { + log_warning("TLS certificate check failed: " << tls_exception.what()); + if (this->socket_handler->abort_on_invalid_cert()) + throw; + } } + void Basic_Credentials_Manager::load_certs() { + // Only load the certificates the first time + if (Basic_Credentials_Manager::certs_loaded) + return; const std::vector paths = {"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"}; for (const auto& path: paths) { @@ -27,10 +50,12 @@ void Basic_Credentials_Manager::load_certs() while (!bundle.end_of_data() && bundle.check_available(27)) { const Botan::X509_Certificate cert(bundle); - this->certificate_store.add_certificate(cert); + Basic_Credentials_Manager::certificate_store.add_certificate(cert); } } + Basic_Credentials_Manager::certs_loaded = true; } + std::vector Basic_Credentials_Manager::trusted_certificate_authorities(const std::string&, const std::string&) { return {&this->certificate_store}; diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index 8641f1d..e292321 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -8,10 +8,12 @@ #include #include +class TCPSocketHandler; + class Basic_Credentials_Manager: public Botan::Credentials_Manager { public: - Basic_Credentials_Manager(); + Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler); void verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector&) override final; @@ -19,8 +21,11 @@ public: const std::string& context) override final; private: - void load_certs(); - Botan::Certificate_Store_In_Memory certificate_store; + const TCPSocketHandler* const socket_handler; + + static void load_certs(); + static Botan::Certificate_Store_In_Memory certificate_store; + static bool certs_loaded; }; #endif //BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 81a36ef..0ed74a2 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -19,7 +19,6 @@ # include Botan::AutoSeeded_RNG TCPSocketHandler::rng; -Basic_Credentials_Manager TCPSocketHandler::credential_manager; Botan::TLS::Policy TCPSocketHandler::policy; Botan::TLS::Session_Manager_In_Memory TCPSocketHandler::session_manager(TCPSocketHandler::rng); @@ -40,6 +39,9 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): connected(false), connecting(false), hostname_resolution_failed(false) +#ifdef BOTAN_FOUND + ,credential_manager(this) +#endif {} void TCPSocketHandler::init_socket(const struct addrinfo* rp) @@ -369,7 +371,7 @@ void TCPSocketHandler::start_tls() std::bind(&TCPSocketHandler::tls_data_cb, this, ph::_1, ph::_2), std::bind(&TCPSocketHandler::tls_alert_cb, this, ph::_1, ph::_2, ph::_3), std::bind(&TCPSocketHandler::tls_handshake_cb, this, ph::_1), - session_manager, credential_manager, policy, + session_manager, this->credential_manager, policy, rng, server_info, Botan::TLS::Protocol_Version::latest_tls_version()); } diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index d173c1f..213e286 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -90,6 +90,16 @@ public: * The size argument is the size of the last chunk of data that was added to the buffer. */ virtual void parse_in_buffer(const size_t size) = 0; +#ifdef BOTAN_FOUND + /** + * Tell whether the credential manager should cancel the connection when the + * certificate is invalid. + */ + virtual bool abort_on_invalid_cert() const + { + return true; + } +#endif bool is_connected() const override final; bool is_connecting() const; @@ -230,9 +240,9 @@ private: * Botan stuff to manipulate a TLS session. */ static Botan::AutoSeeded_RNG rng; - static Basic_Credentials_Manager credential_manager; static Botan::TLS::Policy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; + Basic_Credentials_Manager credential_manager; /** * 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 7caf443..93ea2ae 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -962,3 +962,14 @@ void IrcClient::leave_dummy_channel(const std::string& exit_message) this->dummy_channel.remove_all_users(); this->bridge->send_muc_leave(Iid("%"s + this->hostname), std::string(this->current_nick), exit_message, true); } + +#ifdef BOTAN_FOUND +bool IrcClient::abort_on_invalid_cert() const +{ +#ifdef USE_DATABASE + auto options = Database::get_irc_server_options(this->bridge->get_bare_jid(), this->hostname); + return options.verifyCert.value(); +#endif + return true; +} +#endif diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index cdae0aa..733fc92 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -52,6 +52,9 @@ public: * complete messages from it. */ void parse_in_buffer(const size_t) override final; +#ifdef BOTAN_FOUND + virtual bool abort_on_invalid_cert() const override final; +#endif /** * Return the channel with this name, create it if it does not yet exist */ diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index fa3a35c..ff0c8d4 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -162,6 +162,19 @@ void ConfigureIrcServerStep1(XmppComponent*, AdhocSession& session, XmlNode& com } tls_ports.add_child(required); x.add_child(std::move(tls_ports)); + + XmlNode verify_cert("field"); + verify_cert["var"] = "verify_cert"; + verify_cert["type"] = "boolean"; + verify_cert["label"] = "Verify certificate"; + verify_cert["desc"] = "Whether or not to abort the connection if the server’s TLS certificate is invalid"; + XmlNode verify_cert_value("value"); + if (options.verifyCert.value()) + verify_cert_value.set_inner("true"); + else + verify_cert_value.set_inner("false"); + verify_cert.add_child(std::move(verify_cert_value)); + x.add_child(std::move(verify_cert)); #endif XmlNode pass("field"); @@ -252,6 +265,13 @@ void ConfigureIrcServerStep2(XmppComponent*, AdhocSession& session, XmlNode& com ports += val->get_inner() + ";"; options.tlsPorts = ports; } + + else if (field->get_tag("var") == "verify_cert" && value + && !value->get_inner().empty()) + { + auto val = to_bool(value->get_inner()); + options.verifyCert = val; + } #endif // BOTAN_FOUND else if (field->get_tag("var") == "pass" && -- cgit v1.2.3