biboumi: Update botan to version 3
The botan dependency has introduced a number of breaking changes with version 3, a couple of which impact biboumi as well This patch makes biboumi compatible with botan 3 and drops support for botan 2 at the same time As a consequence of the botan dependency update, C++ version has to be upgraded to C++ 20 https://botan.randombit.net/handbook/migration_guide.html
This commit is contained in:
+1
-1
@@ -30,7 +30,7 @@ find_library(LIBUBSAN NAMES ubsan libubsan.so.0)
|
||||
#
|
||||
## Set various debug flags (instrumentation libs, coverage, …)
|
||||
#
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -pedantic -Wall -Wextra -Wconversion -fvisibility=hidden -fvisibility-inlines-hidden")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -pedantic -Wall -Wextra -Wconversion -fvisibility=hidden -fvisibility-inlines-hidden")
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage --coverage")
|
||||
endif()
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ Biboumi is an XMPP gateway that connects to IRC servers and translates
|
||||
between the two protocols. It can be used to access IRC channels using any
|
||||
XMPP client as if these channels were XMPP MUCs.
|
||||
|
||||
It is written in modern C++14 and makes great efforts to have as little
|
||||
It is written in modern C++20 and makes great efforts to have as little
|
||||
dependencies and to be as simple as possible.
|
||||
|
||||
The goal is to provide a way to access most of IRC features using any XMPP
|
||||
|
||||
@@ -5,40 +5,63 @@
|
||||
# BOTAN_FOUND - True if library and include directory are found
|
||||
# If set to TRUE, the following are also defined:
|
||||
# BOTAN_INCLUDE_DIRS - The directory where to find the header file
|
||||
# BOTAN_LIBRARIES - Where to find the library file
|
||||
#
|
||||
# For conveniance, these variables are also set. They have the same values
|
||||
# than the variables above. The user can thus choose his/her prefered way
|
||||
# to write them.
|
||||
# BOTAN_LIBRARY
|
||||
# BOTAN_INCLUDE_DIR
|
||||
# BOTAN_LIBRARIES - Where to find the library files
|
||||
#
|
||||
# This file is in the public domain
|
||||
# Copied and modified from
|
||||
# https://github.com/vistle/vistle/blob/master/cmake/Modules/FindBOTAN.cmake
|
||||
|
||||
include(FindPkgConfig)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
if(NOT BOTAN_FOUND)
|
||||
pkg_check_modules(BOTAN botan-2)
|
||||
set(BOTAN_VERSIONS botan-3)
|
||||
set(BOTAN_NAMES botan-3 botan)
|
||||
set(BOTAN_NAMES_DEBUG botand-3 botand)
|
||||
|
||||
find_path(
|
||||
BOTAN_INCLUDE_DIR
|
||||
NAMES botan/build.h
|
||||
PATH_SUFFIXES ${BOTAN_VERSIONS}
|
||||
DOC "The Botan include directory")
|
||||
if(BOTAN_INCLUDE_DIR)
|
||||
file(READ "${BOTAN_INCLUDE_DIR}/botan/build.h" build)
|
||||
string(REGEX MATCH "BOTAN_VERSION_MAJOR ([0-9]*)" _ ${build})
|
||||
set(BOTAN_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
string(REGEX MATCH "BOTAN_VERSION_MINOR ([0-9]*)" _ ${build})
|
||||
set(BOTAN_VERSION_MINOR ${CMAKE_MATCH_1})
|
||||
string(REGEX MATCH "BOTAN_VERSION_PATCH ([0-9]*)" _ ${build})
|
||||
set(BOTAN_VERSION_PATCH ${CMAKE_MATCH_1})
|
||||
set(BOTAN_VERSION "${BOTAN_VERSION_MAJOR}.${BOTAN_VERSION_MINOR}.${BOTAN_VERSION_PATCH}")
|
||||
endif()
|
||||
|
||||
if(NOT BOTAN_FOUND)
|
||||
find_path(BOTAN_INCLUDE_DIRS NAMES botan/botan.h
|
||||
PATH_SUFFIXES botan-2
|
||||
DOC "The botan include directory")
|
||||
find_library(
|
||||
BOTAN_LIBRARY
|
||||
NAMES ${BOTAN_NAMES}
|
||||
PATH_SUFFIXES release/lib lib
|
||||
DOC "The Botan (release) library")
|
||||
if(MSVC)
|
||||
find_library(
|
||||
BOTAN_LIBRARY_DEBUG
|
||||
NAMES ${BOTAN_NAMES_DEBUG}
|
||||
PATH_SUFFIXES debug/lib lib
|
||||
DOC "The Botan debug library")
|
||||
find_package_handle_standard_args(
|
||||
BOTAN
|
||||
REQUIRED_VARS BOTAN_LIBRARY BOTAN_LIBRARY_DEBUG BOTAN_INCLUDE_DIR
|
||||
VERSION_VAR BOTAN_VERSION)
|
||||
else()
|
||||
find_package_handle_standard_args(
|
||||
BOTAN
|
||||
REQUIRED_VARS BOTAN_LIBRARY BOTAN_INCLUDE_DIR
|
||||
VERSION_VAR BOTAN_VERSION)
|
||||
endif()
|
||||
|
||||
find_library(BOTAN_LIBRARIES NAMES botan botan-2
|
||||
DOC "The botan library")
|
||||
|
||||
# Use some standard module to handle the QUIETLY and REQUIRED arguments, and
|
||||
# set BOTAN_FOUND to TRUE if these two variables are set.
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(BOTAN REQUIRED_VARS BOTAN_LIBRARIES BOTAN_INCLUDE_DIRS)
|
||||
|
||||
if(BOTAN_FOUND)
|
||||
set(BOTAN_LIBRARY ${BOTAN_LIBRARIES} CACHE INTERNAL "")
|
||||
set(BOTAN_INCLUDE_DIR ${BOTAN_INCLUDE_DIRS} CACHE INTERNAL "")
|
||||
set(BOTAN_FOUND ${BOTAN_FOUND} CACHE INTERNAL "")
|
||||
if(BOTAN_FOUND)
|
||||
set(BOTAN_INCLUDE_DIRS ${BOTAN_INCLUDE_DIR})
|
||||
if(MSVC)
|
||||
set(BOTAN_LIBRARIES optimized ${BOTAN_LIBRARY} debug ${BOTAN_LIBRARY_DEBUG})
|
||||
else()
|
||||
set(BOTAN_LIBRARIES ${BOTAN_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(BOTAN_INCLUDE_DIRS BOTAN_LIBRARIES)
|
||||
mark_as_advanced(BOTAN_INCLUDE_DIR BOTAN_LIBRARY BOTAN_LIBRARY_DEBUG)
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ biboumi by having all dependencies.
|
||||
Tools:
|
||||
~~~~~~
|
||||
|
||||
- A C++14 compiler (clang >= 3.4 or gcc >= 5.0 for example)
|
||||
- A C++20 compiler (clang >= 14 or gcc >= 11.2 for example)
|
||||
- CMake
|
||||
- sphinx (optional) to build the documentation
|
||||
|
||||
@@ -51,7 +51,7 @@ udns_ (optional, but recommended)
|
||||
performances when connecting to a big number of IRC servers at the same
|
||||
time.
|
||||
|
||||
libbotan_ 2.x (optional, but recommended)
|
||||
libbotan_ 3.x (optional, but recommended)
|
||||
Provides TLS support. Without it, IRC connections are all made in
|
||||
plain-text mode.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ udns-dev \
|
||||
expat-dev \
|
||||
libidn-dev \
|
||||
sqlite-dev \
|
||||
botan-dev \
|
||||
botan3-dev \
|
||||
util-linux-dev \
|
||||
libgcrypt-dev \
|
||||
postgresql-dev \
|
||||
|
||||
@@ -16,7 +16,7 @@ libudns-dev \
|
||||
libexpat1-dev \
|
||||
libidn11-dev \
|
||||
libsqlite3-dev \
|
||||
libbotan-2-dev \
|
||||
libbotan-3-dev \
|
||||
libsystemd-dev \
|
||||
uuid-dev \
|
||||
libgcrypt20-dev \
|
||||
|
||||
@@ -5,6 +5,7 @@ FROM fedora:latest
|
||||
|
||||
ENV LC_ALL=C.UTF-8
|
||||
|
||||
# TODO: upgrade to botan3
|
||||
RUN dnf --refresh install -y \
|
||||
git \
|
||||
make \
|
||||
|
||||
@@ -213,7 +213,7 @@ void IrcClient::start()
|
||||
auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
|
||||
this->get_hostname());
|
||||
# ifdef BOTAN_FOUND
|
||||
this->credential_manager.set_trusted_fingerprint(options.col<Database::TrustedFingerprint>());
|
||||
this->credential_manager->set_trusted_fingerprint(options.col<Database::TrustedFingerprint>());
|
||||
# endif
|
||||
if (Config::get("fixed_irc_server", "").empty() &&
|
||||
!options.col<Database::Address>().empty())
|
||||
|
||||
@@ -39,7 +39,7 @@ const std::string& BasicCredentialsManager::get_trusted_fingerprint() const
|
||||
}
|
||||
|
||||
void check_tls_certificate(const std::vector<Botan::X509_Certificate>& certs,
|
||||
const std::string& hostname, const std::string& trusted_fingerprint,
|
||||
const std::string_view hostname, const std::string& trusted_fingerprint,
|
||||
const std::exception_ptr& exc)
|
||||
{
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class TCPSocketHandler;
|
||||
* on a trusted fingerprint.
|
||||
*/
|
||||
void check_tls_certificate(const std::vector<Botan::X509_Certificate>& certs,
|
||||
const std::string& hostname, const std::string& trusted_fingerprint,
|
||||
const std::string_view hostname, const std::string& trusted_fingerprint,
|
||||
const std::exception_ptr& exc);
|
||||
|
||||
class BasicCredentialsManager: public Botan::Credentials_Manager
|
||||
|
||||
@@ -16,23 +16,29 @@
|
||||
# include <botan/hex.h>
|
||||
# include <botan/auto_rng.h>
|
||||
# include <botan/tls_exceptn.h>
|
||||
# include <botan/tls_session_manager_memory.h>
|
||||
# include <botan/tls_session_manager_hybrid.h>
|
||||
# include <config/config.hpp>
|
||||
# include <utils/dirname.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
Botan::AutoSeeded_RNG& get_rng()
|
||||
std::shared_ptr<Botan::RandomNumberGenerator>& get_rng()
|
||||
{
|
||||
static Botan::AutoSeeded_RNG rng{};
|
||||
static std::shared_ptr<Botan::RandomNumberGenerator> rng;
|
||||
|
||||
if (!rng)
|
||||
rng = std::make_shared<Botan::AutoSeeded_RNG>();
|
||||
return rng;
|
||||
}
|
||||
Botan::TLS::Session_Manager_In_Memory& get_session_manager()
|
||||
std::shared_ptr<Botan::TLS::Session_Manager>& get_session_manager(std::shared_ptr<BasicCredentialsManager> creds)
|
||||
{
|
||||
static Botan::TLS::Session_Manager_In_Memory session_manager{get_rng()};
|
||||
#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(2,4,0)
|
||||
// workaround for https://github.com/randombit/botan/issues/1276
|
||||
session_manager.remove_all();
|
||||
#endif
|
||||
static std::shared_ptr<Botan::TLS::Session_Manager> session_manager;
|
||||
|
||||
if (!session_manager)
|
||||
session_manager = std::make_shared<Botan::TLS::Session_Manager_Hybrid>(
|
||||
std::make_unique<Botan::TLS::Session_Manager_In_Memory>(get_rng()),
|
||||
creds, get_rng());
|
||||
return session_manager;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +56,8 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr<Poller>& poller):
|
||||
SocketHandler(poller, -1),
|
||||
use_tls(false)
|
||||
#ifdef BOTAN_FOUND
|
||||
,credential_manager()
|
||||
,credential_manager(std::make_shared<BasicCredentialsManager>())
|
||||
,policy(std::make_shared<BiboumiTLSPolicy>())
|
||||
#endif
|
||||
{}
|
||||
|
||||
@@ -242,11 +249,11 @@ void TCPSocketHandler::start_tls(const std::string& address, const std::string&
|
||||
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->policy->load(policy_directory + "policy.txt");
|
||||
this->policy->load(policy_directory + address + ".policy.txt");
|
||||
this->tls = std::make_unique<Botan::TLS::Client>(
|
||||
*this,
|
||||
get_session_manager(), this->credential_manager, this->policy,
|
||||
shared_from_this(),
|
||||
get_session_manager(this->credential_manager), this->credential_manager, this->policy,
|
||||
get_rng(), server_info, Botan::TLS::Protocol_Version::latest_tls_version());
|
||||
}
|
||||
|
||||
@@ -298,17 +305,16 @@ void TCPSocketHandler::tls_send(std::string&& data)
|
||||
std::make_move_iterator(data.end()));
|
||||
}
|
||||
|
||||
void TCPSocketHandler::tls_record_received(uint64_t, const Botan::byte *data, size_t size)
|
||||
void TCPSocketHandler::tls_record_received(uint64_t, std::span<const uint8_t> data)
|
||||
{
|
||||
this->in_buf += std::string(reinterpret_cast<const char*>(data),
|
||||
size);
|
||||
this->in_buf += std::string(data.begin(), data.end());
|
||||
if (!this->in_buf.empty())
|
||||
this->parse_in_buffer(size);
|
||||
this->parse_in_buffer(data.size());
|
||||
}
|
||||
|
||||
void TCPSocketHandler::tls_emit_data(const Botan::byte *data, size_t size)
|
||||
void TCPSocketHandler::tls_emit_data(std::span<const uint8_t> data)
|
||||
{
|
||||
this->raw_send(std::string(reinterpret_cast<const char*>(data), size));
|
||||
this->raw_send(std::string(data.begin(), data.end()));
|
||||
}
|
||||
|
||||
void TCPSocketHandler::tls_alert(Botan::TLS::Alert alert)
|
||||
@@ -316,25 +322,24 @@ void TCPSocketHandler::tls_alert(Botan::TLS::Alert alert)
|
||||
log_debug("tls_alert: ", alert.type_string());
|
||||
}
|
||||
|
||||
bool TCPSocketHandler::tls_session_established(const Botan::TLS::Session& session)
|
||||
void TCPSocketHandler::tls_session_established(const Botan::TLS::Session_Summary& session)
|
||||
{
|
||||
log_debug("Handshake with ", session.server_info().hostname(), " complete.",
|
||||
" Version: ", session.version().to_string(),
|
||||
" using ", session.ciphersuite().to_string());
|
||||
if (!session.session_id().empty())
|
||||
log_debug("Session ID ", Botan::hex_encode(session.session_id()));
|
||||
if (!session.session_ticket().empty())
|
||||
log_debug("Session ticket ", Botan::hex_encode(session.session_ticket()));
|
||||
return true;
|
||||
if (session.session_ticket().has_value())
|
||||
log_debug("Session ticket ", Botan::hex_encode(*session.session_ticket()));
|
||||
}
|
||||
|
||||
void TCPSocketHandler::tls_verify_cert_chain(const std::vector<Botan::X509_Certificate>& cert_chain,
|
||||
const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp_responses,
|
||||
const std::vector<std::optional<Botan::OCSP::Response>>& ocsp_responses,
|
||||
const std::vector<Botan::Certificate_Store*>& trusted_roots,
|
||||
Botan::Usage_Type usage, const std::string& hostname,
|
||||
Botan::Usage_Type usage, std::string_view hostname,
|
||||
const Botan::TLS::Policy& policy)
|
||||
{
|
||||
if (!this->policy.verify_certificate)
|
||||
if (!this->policy->verify_certificate)
|
||||
{
|
||||
log_debug("Not verifying certificate due to domain policy ");
|
||||
return;
|
||||
@@ -352,7 +357,7 @@ void TCPSocketHandler::tls_verify_cert_chain(const std::vector<Botan::X509_Certi
|
||||
if (this->abort_on_invalid_cert())
|
||||
exception_ptr = std::current_exception();
|
||||
|
||||
check_tls_certificate(cert_chain, hostname, this->credential_manager.get_trusted_fingerprint(), exception_ptr);
|
||||
check_tls_certificate(cert_chain, hostname, this->credential_manager->get_trusted_fingerprint(), exception_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
* But doesn’t do any connect() or accept() or anything else.
|
||||
*/
|
||||
class TCPSocketHandler: public SocketHandler
|
||||
,public std::enable_shared_from_this<TCPSocketHandler>
|
||||
#ifdef BOTAN_FOUND
|
||||
,public Botan::TLS::Callbacks
|
||||
#endif
|
||||
@@ -137,12 +138,12 @@ private:
|
||||
* Called by the tls object that some data has been decrypt. We call
|
||||
* parse_in_buffer() to handle that unencrypted data.
|
||||
*/
|
||||
void tls_record_received(uint64_t rec_no, const Botan::byte* data, size_t size) override final;
|
||||
void tls_record_received(uint64_t rec_no, std::span<const uint8_t> data) override final;
|
||||
/**
|
||||
* Called by the tls object to indicate that some data has been encrypted
|
||||
* and is now ready to be sent on the socket as is.
|
||||
*/
|
||||
void tls_emit_data(const Botan::byte* data, size_t size) override final;
|
||||
void tls_emit_data(std::span<const uint8_t> data) override final;
|
||||
/**
|
||||
* Called by the tls object to indicate that a TLS alert has been
|
||||
* received. We don’t use it, we just log some message, at the moment.
|
||||
@@ -152,13 +153,13 @@ private:
|
||||
* Called by the tls object at the end of the TLS handshake. We don't do
|
||||
* anything here appart from logging the TLS session information.
|
||||
*/
|
||||
bool tls_session_established(const Botan::TLS::Session& session) override final;
|
||||
void tls_session_established(const Botan::TLS::Session_Summary& session) override final;
|
||||
|
||||
void tls_verify_cert_chain(const std::vector<Botan::X509_Certificate>& cert_chain,
|
||||
const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp_responses,
|
||||
const std::vector<std::optional<Botan::OCSP::Response>>& ocsp_responses,
|
||||
const std::vector<Botan::Certificate_Store*>& trusted_roots,
|
||||
Botan::Usage_Type usage,
|
||||
const std::string& hostname,
|
||||
std::string_view hostname,
|
||||
const Botan::TLS::Policy& policy) override final;
|
||||
/**
|
||||
* Called whenever the tls session goes from inactive to active. This
|
||||
@@ -203,9 +204,9 @@ protected:
|
||||
|
||||
#ifdef BOTAN_FOUND
|
||||
protected:
|
||||
BasicCredentialsManager credential_manager;
|
||||
std::shared_ptr<BasicCredentialsManager> credential_manager;
|
||||
private:
|
||||
BiboumiTLSPolicy policy;
|
||||
std::shared_ptr<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
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <network/tls_policy.hpp>
|
||||
#include <logger/logger.hpp>
|
||||
#include <botan/parsing.h>
|
||||
#include <botan/exceptn.h>
|
||||
|
||||
bool BiboumiTLSPolicy::load(const std::string& filename)
|
||||
@@ -31,7 +30,7 @@ bool BiboumiTLSPolicy::load(const std::string& filename)
|
||||
|
||||
void BiboumiTLSPolicy::load(std::istream& is)
|
||||
{
|
||||
const auto dict = Botan::read_cfg(is);
|
||||
const auto dict = BiboumiTLSPolicy::read_cfg(is);
|
||||
for (const auto& pair: dict)
|
||||
{
|
||||
// Workaround for options that are not overridden in Botan::TLS::Text_Policy
|
||||
@@ -49,4 +48,56 @@ bool BiboumiTLSPolicy::require_cert_revocation_info() const
|
||||
return this->req_cert_revocation_info;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> BiboumiTLSPolicy::read_cfg(std::istream& is) {
|
||||
std::map<std::string, std::string> kv;
|
||||
size_t line = 0;
|
||||
|
||||
while(is.good()) {
|
||||
std::string s;
|
||||
|
||||
std::getline(is, s);
|
||||
|
||||
++line;
|
||||
|
||||
if(s.empty() || s[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
s = clean_ws(s.substr(0, s.find('#')));
|
||||
|
||||
if(s.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto eq = s.find('=');
|
||||
|
||||
if(eq == std::string::npos || eq == 0 || eq == s.size() - 1) {
|
||||
throw Botan::Decoding_Error("Bad read_cfg input '" + s + "' on line " + std::to_string(line));
|
||||
}
|
||||
|
||||
const std::string key = BiboumiTLSPolicy::clean_ws(s.substr(0, eq));
|
||||
const std::string val = BiboumiTLSPolicy::clean_ws(s.substr(eq + 1, std::string::npos));
|
||||
|
||||
kv[key] = val;
|
||||
}
|
||||
|
||||
return kv;
|
||||
}
|
||||
|
||||
std::string BiboumiTLSPolicy::clean_ws(std::string_view s) {
|
||||
const char* ws = " \t\n";
|
||||
auto start = s.find_first_not_of(ws);
|
||||
auto end = s.find_last_not_of(ws);
|
||||
|
||||
if(start == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if(end == std::string::npos) {
|
||||
return std::string(s.substr(start, end));
|
||||
} else {
|
||||
return std::string(s.substr(start, start + end + 1));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,6 +24,12 @@ public:
|
||||
bool verify_certificate{true};
|
||||
protected:
|
||||
bool req_cert_revocation_info{true};
|
||||
private:
|
||||
// Copied from botan-3's src/lib/utils/read_cfg.cpp
|
||||
// These were made internal in v3, but they are in use by biboumi so they have
|
||||
// to be vendored in
|
||||
static std::map<std::string, std::string> read_cfg(std::istream& is);
|
||||
static std::string clean_ws(std::string_view s);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user