summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.rst2
-rw-r--r--CMakeLists.txt2
-rw-r--r--conf/irc.freenode.net.policy.txt1
-rw-r--r--conf/irc.mozilla.org.policy.txt1
-rw-r--r--conf/policy.txt2
-rw-r--r--doc/biboumi.1.rst35
-rw-r--r--src/network/tcp_socket_handler.cpp14
-rw-r--r--src/network/tcp_socket_handler.hpp17
-rw-r--r--src/network/tls_policy.cpp48
-rw-r--r--src/network/tls_policy.hpp28
-rw-r--r--src/utils/dirname.cpp16
-rw-r--r--src/utils/dirname.hpp6
-rw-r--r--src/utils/xdg.hpp2
-rw-r--r--tests/network.cpp42
-rw-r--r--tests/utils.cpp15
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("./") == "./");
+}