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:
Luca Matei Pintilie
2025-07-29 15:53:20 +02:00
parent 8c4769e27b
commit e4d32f9392
14 changed files with 161 additions and 74 deletions
+1 -1
View File
@@ -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())
+1 -1
View File
@@ -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)
{
+1 -1
View File
@@ -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
+32 -27
View File
@@ -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);
}
}
+8 -7
View File
@@ -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
+53 -2
View File
@@ -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
+6
View File
@@ -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