diff options
-rw-r--r-- | CHANGELOG.rst | 2 | ||||
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | conf/irc.freenode.net.policy.txt | 1 | ||||
-rw-r--r-- | conf/irc.mozilla.org.policy.txt | 1 | ||||
-rw-r--r-- | conf/policy.txt | 2 | ||||
-rw-r--r-- | doc/biboumi.1.rst | 35 | ||||
-rw-r--r-- | src/network/tcp_socket_handler.cpp | 14 | ||||
-rw-r--r-- | src/network/tcp_socket_handler.hpp | 17 | ||||
-rw-r--r-- | src/network/tls_policy.cpp | 48 | ||||
-rw-r--r-- | src/network/tls_policy.hpp | 28 | ||||
-rw-r--r-- | src/utils/dirname.cpp | 16 | ||||
-rw-r--r-- | src/utils/dirname.hpp | 6 | ||||
-rw-r--r-- | src/utils/xdg.hpp | 2 | ||||
-rw-r--r-- | tests/network.cpp | 42 | ||||
-rw-r--r-- | tests/utils.cpp | 15 |
15 files changed, 207 insertions, 24 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1e94d6c..39941eb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,8 @@ Version 5.0 messages, instead of the whole archive. - Multiline topics are now properly handled - Configuration options can be overridden by values found in the process env. + - Botan’s TLS policies can be customized by the administrator, for each + IRC server, with simple text files. Version 4.1 - 2017-03-21 ======================== diff --git a/CMakeLists.txt b/CMakeLists.txt index 41260aa..37e8908 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -336,6 +336,8 @@ install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) install(FILES ${MAN_PAGE} DESTINATION share/man/man1 OPTIONAL COMPONENT documentation) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/biboumi.service DESTINATION lib/systemd/system COMPONENT init) install(FILES conf/biboumi.cfg DESTINATION /etc/biboumi COMPONENT configuration) +file(GLOB policy_files conf/*policy.txt) +install(FILES ${policy_files} DESTINATION /etc/biboumi COMPONENT configuration) # ## Dist target diff --git a/conf/irc.freenode.net.policy.txt b/conf/irc.freenode.net.policy.txt new file mode 100644 index 0000000..aa26fe5 --- /dev/null +++ b/conf/irc.freenode.net.policy.txt @@ -0,0 +1 @@ +require_cert_revocation_info = false diff --git a/conf/irc.mozilla.org.policy.txt b/conf/irc.mozilla.org.policy.txt new file mode 100644 index 0000000..f099e61 --- /dev/null +++ b/conf/irc.mozilla.org.policy.txt @@ -0,0 +1 @@ +minimum_dh_group_size = 1024 diff --git a/conf/policy.txt b/conf/policy.txt new file mode 100644 index 0000000..8edc09f --- /dev/null +++ b/conf/policy.txt @@ -0,0 +1,2 @@ +require_cert_revocation_info = false +use_ecc_point_compression = true diff --git a/doc/biboumi.1.rst b/doc/biboumi.1.rst index 6e93735..04401eb 100644 --- a/doc/biboumi.1.rst +++ b/doc/biboumi.1.rst @@ -163,6 +163,40 @@ identd_port The TCP port on which to listen for identd queries. The default is the standard value: 113. +policy_directory +---------------- + +A directory that should contain the policy files, used to customize +Botan’s behaviour when negociating the TLS connections with the IRC +servers. If not specified, the directory is the one where biboumi’s +configuration file is located: for example if biboumi reads its +configuration from /etc/biboumi/biboumi.cfg, the policy_directory value +will be /etc/biboumi. + + +TLS configuration +================= + +Various settings of the TLS connections can be customized using policy +files. The files should be located in the directory specified by the +configuration option `policy_directory`_. When attempting to connect to +an IRC server using TLS, biboumi will use Botan’s default TLS policy, and +then will try to load some policy files to override the values found in +these files. For example, if policy_directory is /etc/biboumi, when +trying to connect to irc.example.com, biboumi will try to read +/etc/biboumi/policy.txt, use the values found to override the default +values, then it will try to read /etc/biboumi/irc.example.com.policy.txt +and re-override the policy with the values found in this file. + +The policy.txt file applies to all the connections, and +irc.example.policy.txt will only apply (in addition to policy.txt) when +connecting to that specific server. + +To see the list of possible options to configure, refer to `Botan’s TLS +documentation <https://botan.randombit.net/manual/tls.html#tls-policies>`_. + +By default, biboumi provides a few policy files, to work around some +issues found with a few well-known IRC servers. Usage ===== @@ -628,3 +662,4 @@ protection against flood or any sort of abuse that your users may cause on the IRC servers. Some XMPP server however offer the possibility to restrict what JID can access a gateway. Use that feature if you wish to grant access to your biboumi instance only to a list of trusted users. + diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp index 02265ec..1bd5315 100644 --- a/src/network/tcp_socket_handler.cpp +++ b/src/network/tcp_socket_handler.cpp @@ -14,6 +14,8 @@ #ifdef BOTAN_FOUND # include <botan/hex.h> # include <botan/tls_exceptn.h> +# include <config/config.hpp> +# include <utils/dirname.hpp> namespace { @@ -22,11 +24,6 @@ namespace static Botan::AutoSeeded_RNG rng{}; return rng; } - BiboumiTLSPolicy& get_policy() - { - static BiboumiTLSPolicy policy{}; - return policy; - } Botan::TLS::Session_Manager_In_Memory& get_session_manager() { static Botan::TLS::Session_Manager_In_Memory session_manager{get_rng()}; @@ -233,6 +230,11 @@ void TCPSocketHandler::consume_in_buffer(const std::size_t size) void TCPSocketHandler::start_tls(const std::string& address, const std::string& port) { Botan::TLS::Server_Information server_info(address, "irc", std::stoul(port)); + auto policy_directory = Config::get("policy_directory", utils::dirname(Config::get_filename())); + if (!policy_directory.empty() && policy_directory[policy_directory.size()-1] != '/') + policy_directory += '/'; + this->policy.load(policy_directory + "policy.txt"); + this->policy.load(policy_directory + address + ".policy.txt"); this->tls = std::make_unique<Botan::TLS::Client>( # if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) *this, @@ -242,7 +244,7 @@ void TCPSocketHandler::start_tls(const std::string& address, const std::string& [this](Botan::TLS::Alert alert, const Botan::byte*, size_t) { this->tls_alert(alert); }, [this](const Botan::TLS::Session& session) { return this->tls_session_established(session); }, # endif - get_session_manager(), this->credential_manager, get_policy(), + get_session_manager(), this->credential_manager, this->policy, get_rng(), server_info, Botan::TLS::Protocol_Version::latest_tls_version()); } diff --git a/src/network/tcp_socket_handler.hpp b/src/network/tcp_socket_handler.hpp index ba23861..f68698e 100644 --- a/src/network/tcp_socket_handler.hpp +++ b/src/network/tcp_socket_handler.hpp @@ -23,21 +23,7 @@ # include <botan/types.h> # include <botan/botan.h> # include <botan/tls_session_manager.h> - -class BiboumiTLSPolicy: public Botan::TLS::Policy -{ -public: -# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,33) - bool use_ecc_point_compression() const override - { - return true; - } - bool require_cert_revocation_info() const override - { - return false; - } -# endif -}; +# include <network/tls_policy.hpp> # if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) # define BOTAN_TLS_CALLBACKS_OVERRIDE override final @@ -230,6 +216,7 @@ protected: protected: BasicCredentialsManager credential_manager; private: + BiboumiTLSPolicy policy; /** * 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/network/tls_policy.cpp b/src/network/tls_policy.cpp new file mode 100644 index 0000000..5439397 --- /dev/null +++ b/src/network/tls_policy.cpp @@ -0,0 +1,48 @@ +#include "biboumi.h" + +#ifdef BOTAN_FOUND + +#include <fstream> + +#include <utils/tolower.hpp> + +#include <network/tls_policy.hpp> +#include <logger/logger.hpp> + +bool BiboumiTLSPolicy::load(const std::string& filename) +{ + std::ifstream is(filename.data()); + if (is) + { + try { + this->load(is); + log_info("Successfully loaded policy file: ", filename); + return true; + } catch (const Botan::Exception& e) { + log_error("Failed to parse policy_file ", filename, ": ", e.what()); + return false; + } + } + log_info("Could not open policy file: ", filename); + return false; +} + +void BiboumiTLSPolicy::load(std::istream& is) +{ + const auto dict = Botan::read_cfg(is); + for (const auto& pair: dict) + { + // Workaround for options that are not overridden in Botan::TLS::Text_Policy + if (pair.first == "require_cert_revocation_info") + this->req_cert_revocation_info = !(pair.second == "0" || utils::tolower(pair.second) == "false"); + else + this->set(pair.first, pair.second); + } +} + +bool BiboumiTLSPolicy::require_cert_revocation_info() const +{ + return this->req_cert_revocation_info; +} + +#endif diff --git a/src/network/tls_policy.hpp b/src/network/tls_policy.hpp new file mode 100644 index 0000000..29fd2b3 --- /dev/null +++ b/src/network/tls_policy.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "biboumi.h" + +#ifdef BOTAN_FOUND + +#include <botan/tls_policy.h> + +class BiboumiTLSPolicy: public Botan::TLS::Text_Policy +{ +public: + BiboumiTLSPolicy(): + Botan::TLS::Text_Policy({}) + {} + bool load(const std::string& filename); + void load(std::istream& iss); + + BiboumiTLSPolicy(const BiboumiTLSPolicy &) = delete; + BiboumiTLSPolicy(BiboumiTLSPolicy &&) = delete; + BiboumiTLSPolicy &operator=(const BiboumiTLSPolicy &) = delete; + BiboumiTLSPolicy &operator=(BiboumiTLSPolicy &&) = delete; + + bool require_cert_revocation_info() const override; +protected: + bool req_cert_revocation_info{true}; +}; + +#endif diff --git a/src/utils/dirname.cpp b/src/utils/dirname.cpp new file mode 100644 index 0000000..71c9c38 --- /dev/null +++ b/src/utils/dirname.cpp @@ -0,0 +1,16 @@ +#include <utils/dirname.hpp> + +namespace utils +{ + std::string dirname(const std::string filename) + { + if (filename.empty()) + return "./"; + if (filename == ".." || filename == ".") + return filename; + auto pos = filename.rfind('/'); + if (pos == std::string::npos) + return "./"; + return filename.substr(0, pos + 1); + } +} diff --git a/src/utils/dirname.hpp b/src/utils/dirname.hpp new file mode 100644 index 0000000..c1df81b --- /dev/null +++ b/src/utils/dirname.hpp @@ -0,0 +1,6 @@ +#include <string> + +namespace utils +{ +std::string dirname(const std::string filename); +} diff --git a/src/utils/xdg.hpp b/src/utils/xdg.hpp index 56e11da..7be6922 100644 --- a/src/utils/xdg.hpp +++ b/src/utils/xdg.hpp @@ -10,5 +10,3 @@ */ std::string xdg_config_path(const std::string& filename); std::string xdg_data_path(const std::string& filename); - - diff --git a/tests/network.cpp b/tests/network.cpp new file mode 100644 index 0000000..52e9ae3 --- /dev/null +++ b/tests/network.cpp @@ -0,0 +1,42 @@ +#include "catch.hpp" +#include <network/tls_policy.hpp> + +TEST_CASE("tls_policy") +{ + BiboumiTLSPolicy policy; + const auto default_minimum_signature_strength = policy.minimum_signature_strength(); + const auto default_session_ticket_lifetime = policy.session_ticket_lifetime(); + const auto default_minimum_rsa_bits = policy.minimum_rsa_bits(); + + policy.load("does not exist"); + WHEN("we fail to load the file") + { + THEN("all values are the default ones") + { + CHECK(policy.minimum_signature_strength() == default_minimum_signature_strength); + CHECK(policy.minimum_rsa_bits() == default_minimum_rsa_bits); + } + AND_WHEN("we load a valid first file") + { + std::istringstream iss("minimum_signature_strength = 128\nminimum_rsa_bits=12\n"); + policy.load(iss); + THEN("the specified values are updated, and the rest is still the default") + { + CHECK(policy.minimum_signature_strength() == 128); + CHECK(policy.minimum_rsa_bits() == 12); + CHECK(policy.session_ticket_lifetime() == default_session_ticket_lifetime); + } + AND_WHEN("we load a second file") + { + std::istringstream iss("minimum_signature_strength = 15"); + policy.load(iss); + THEN("the specified values are updated, and the rest is untouched") + { + CHECK(policy.minimum_signature_strength() == 15); + CHECK(policy.minimum_rsa_bits() == 12); + CHECK(policy.session_ticket_lifetime() == default_session_ticket_lifetime); + } + } + } + } +} diff --git a/tests/utils.cpp b/tests/utils.cpp index b8a3e75..c5ef7e7 100644 --- a/tests/utils.cpp +++ b/tests/utils.cpp @@ -10,6 +10,7 @@ #include <utils/time.hpp> #include <utils/system.hpp> #include <utils/scopeguard.hpp> +#include <utils/dirname.hpp> using namespace std::string_literals; @@ -157,4 +158,16 @@ TEST_CASE("system_name") { CHECK(utils::get_system_name() != "Unknown"); CHECK(!utils::get_system_name().empty()); -}
\ No newline at end of file +} + +TEST_CASE("dirname") +{ + CHECK(utils::dirname("/") == "/"); + CHECK(utils::dirname("coucou.txt") == "./"); + CHECK(utils::dirname("../coucou.txt") == "../"); + CHECK(utils::dirname("/etc/biboumi/coucou.txt") == "/etc/biboumi/"); + CHECK(utils::dirname("..") == ".."); + CHECK(utils::dirname("../") == "../"); + CHECK(utils::dirname(".") == "."); + CHECK(utils::dirname("./") == "./"); +} |