all Ref to support uprefable types

This commit is contained in:
Eric Mertens 2025-01-29 09:54:17 -08:00
parent f5b49ebf66
commit 5801a5404a
7 changed files with 100 additions and 156 deletions

View File

@ -47,7 +47,7 @@ add_executable(xbot
client.cpp client.cpp
connection.cpp connection.cpp
ircmsg.cpp ircmsg.cpp
openssl_errors.cpp openssl_utils.cpp
registration.cpp registration.cpp
sasl_mechanism.cpp sasl_mechanism.cpp
settings.cpp settings.cpp

View File

@ -1,8 +1,9 @@
#include "challenge.hpp" #include "challenge.hpp"
#include "openssl_errors.hpp" #include "openssl_utils.hpp"
#include <mybase64.hpp> #include <mybase64.hpp>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/rsa.h> #include <openssl/rsa.h>
@ -16,13 +17,6 @@ Challenge::Challenge(EVP_PKEY_Ref key, Connection & connection)
, connection_{connection} , connection_{connection}
{} {}
auto Challenge::stop() -> void
{
slot_.disconnect();
buffer_.clear();
}
auto Challenge::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void { auto Challenge::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void {
switch (cmd) { switch (cmd) {
default: break; default: break;
@ -30,42 +24,56 @@ auto Challenge::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void {
buffer_ += msg.args[1]; buffer_ += msg.args[1];
break; break;
case IrcCommand::ERR_NOOPERHOST: case IrcCommand::ERR_NOOPERHOST:
slot_.disconnect();
BOOST_LOG_TRIVIAL(error) << "Challenge: No oper host"; BOOST_LOG_TRIVIAL(error) << "Challenge: No oper host";
stop();
break; break;
case IrcCommand::RPL_YOUREOPER: case IrcCommand::RPL_YOUREOPER:
slot_.disconnect();
BOOST_LOG_TRIVIAL(error) << "Challenge: Already oper"; BOOST_LOG_TRIVIAL(error) << "Challenge: Already oper";
stop();
break; break;
case IrcCommand::RPL_ENDOFRSACHALLENGE2: case IrcCommand::RPL_ENDOFRSACHALLENGE2:
{
slot_.disconnect(); slot_.disconnect();
finish_challenge();
break;
}
}
auto Challenge::finish_challenge() -> void
{
EVP_PKEY_CTX_Ref ctx; EVP_PKEY_CTX_Ref ctx;
unsigned int digestlen = EVP_MAX_MD_SIZE; unsigned int digestlen = EVP_MAX_MD_SIZE;
unsigned char digest[EVP_MAX_MD_SIZE]; unsigned char digest[EVP_MAX_MD_SIZE];
size_t len = mybase64::decoded_size(buffer_.size()); size_t len = mybase64::decoded_size(buffer_.size());
std::vector<unsigned char> ciphertext(len, 0); std::vector<unsigned char> ciphertext(len, 0);
if (not mybase64::decode(buffer_, reinterpret_cast<char*>(ciphertext.data()), &len)) goto error; if (not mybase64::decode(buffer_, reinterpret_cast<char*>(ciphertext.data()), &len))
return log_openssl_errors("Challenge base64::decode: ");
ciphertext.resize(len); ciphertext.resize(len);
// Setup decryption context // Setup decryption context
ctx.reset(EVP_PKEY_CTX_new(key_.get(), nullptr)); ctx.reset(EVP_PKEY_CTX_new(key_.get(), nullptr));
if (ctx.get() == nullptr) goto error; if (ctx.get() == nullptr)
if (1 != EVP_PKEY_decrypt_init(ctx.get())) goto error; return log_openssl_errors("Challenge EVP_PKEY_CTX_new: ");
if (0 >= EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING)) goto error;
if (1 != EVP_PKEY_decrypt_init(ctx.get()))
return log_openssl_errors("Challenge EVP_PKEY_decrypt_init: ");
if (0 >= EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING))
return log_openssl_errors("Challenge EVP_PKEY_CTX_set_rsa_padding: ");
// Determine output size // Determine output size
if (1 != EVP_PKEY_decrypt(ctx.get(), nullptr, &len, ciphertext.data(), ciphertext.size())) goto error; if (1 != EVP_PKEY_decrypt(ctx.get(), nullptr, &len, ciphertext.data(), ciphertext.size()))
return log_openssl_errors("Challenge EVP_PKEY_decrypt (size): ");
buffer_.resize(len); buffer_.resize(len);
// Decrypt ciphertext // Decrypt ciphertext
if (1 != EVP_PKEY_decrypt(ctx.get(), reinterpret_cast<unsigned char*>(buffer_.data()), &len, ciphertext.data(), ciphertext.size())) goto error; if (1 != EVP_PKEY_decrypt(ctx.get(), reinterpret_cast<unsigned char*>(buffer_.data()), &len, ciphertext.data(), ciphertext.size()))
return log_openssl_errors("Challenge EVP_PKEY_decrypt: ");
buffer_.resize(len); buffer_.resize(len);
// Hash the decrypted message // Hash the decrypted message
if (1 != EVP_Digest(buffer_.data(), buffer_.size(), digest, &digestlen, EVP_sha1(), nullptr)) goto error; if (1 != EVP_Digest(buffer_.data(), buffer_.size(), digest, &digestlen, EVP_sha1(), nullptr))
return log_openssl_errors("Challenge EVP_Digest: ");
// Construct reply as '+' and base64 encoded digest // Construct reply as '+' and base64 encoded digest
buffer_.resize(mybase64::encoded_size(digestlen) + 1); buffer_.resize(mybase64::encoded_size(digestlen) + 1);
@ -73,13 +81,8 @@ auto Challenge::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void {
mybase64::encode(std::string_view{(char*)digest, digestlen}, buffer_.data() + 1); mybase64::encode(std::string_view{(char*)digest, digestlen}, buffer_.data() + 1);
connection_.send_challenge(buffer_); connection_.send_challenge(buffer_);
return; connection_.send_ping("oper_up mitigation");
}
error:
log_openssl_errors("Challenge: ");
}
}
}
auto Challenge::start(Connection &connection, const std::string_view user, EVP_PKEY_Ref ref) -> std::shared_ptr<Challenge> auto Challenge::start(Connection &connection, const std::string_view user, EVP_PKEY_Ref ref) -> std::shared_ptr<Challenge>
{ {

View File

@ -16,9 +16,9 @@ class Challenge : std::enable_shared_from_this<Challenge>
std::string buffer_; std::string buffer_;
auto on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void; auto on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void;
auto finish_challenge() -> void;
public: public:
Challenge(EVP_PKEY_Ref, Connection &); Challenge(EVP_PKEY_Ref, Connection &);
auto stop() -> void;
static auto start(Connection &, std::string_view user, EVP_PKEY_Ref) -> std::shared_ptr<Challenge>; static auto start(Connection &, std::string_view user, EVP_PKEY_Ref) -> std::shared_ptr<Challenge>;
}; };

View File

@ -1,9 +1,8 @@
#include "bot.hpp" #include "bot.hpp"
#include "c_callback.hpp"
#include "challenge.hpp" #include "challenge.hpp"
#include "client.hpp" #include "client.hpp"
#include "connection.hpp" #include "connection.hpp"
#include "openssl_errors.hpp" #include "openssl_utils.hpp"
#include "registration.hpp" #include "registration.hpp"
#include "settings.hpp" #include "settings.hpp"
#include "irc_coroutine.hpp" #include "irc_coroutine.hpp"
@ -13,58 +12,12 @@
#include <openssl/pem.h> #include <openssl/pem.h>
#include <cstdio>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
using namespace std::literals; using namespace std::literals;
static auto cert_from_file(const std::string &filename) -> X509_Ref
{
X509_Ref cert;
if (const auto fp = fopen(filename.c_str(), "r"))
{
cert.reset(PEM_read_X509(fp, nullptr, nullptr, nullptr));
if (cert.get() == nullptr)
{
log_openssl_errors("Reading certificate: "sv);
}
fclose(fp);
}
else
{
const auto err = strerror(errno);
BOOST_LOG_TRIVIAL(error) << "Opening certificate: " << err;
}
return cert;
}
static auto key_from_file(const std::string &filename, const std::string_view password) -> EVP_PKEY_Ref
{
EVP_PKEY_Ref key;
if (const auto fp = fopen(filename.c_str(), "r"))
{
auto cb = [password](char * const buf, int const size, int) -> int {
if (size < password.size()) { return -1; }
std::copy(password.begin(), password.end(), buf);
return password.size();
};
key.reset(PEM_read_PrivateKey(fp, nullptr, CCallback<decltype(cb)>::invoke, &cb));
if (key.get() == nullptr)
{
log_openssl_errors("Reading private key: "sv);
}
fclose(fp);
}
else
{
const auto err = strerror(errno);
BOOST_LOG_TRIVIAL(error) << "Opening private key: " << err;
}
return key;
}
static auto start(boost::asio::io_context &io, const Settings &settings) -> void static auto start(boost::asio::io_context &io, const Settings &settings) -> void
{ {
@ -86,30 +39,13 @@ static auto start(boost::asio::io_context &io, const Settings &settings) -> void
const auto bot = Bot::start(client); const auto bot = Bot::start(client);
/* if (not settings.challenge_username.empty() && not settings.challenge_key_file.empty()) {
connection->sig_snote.connect([](auto &match) { if (auto key = key_from_file(settings.challenge_key_file, settings.challenge_key_password)) {
std::cout << "SNOTE " << static_cast<int>(match.get_tag()) << std::endl; client->sig_registered.connect([&settings, connection, key = std::move(key)]() {
for (auto c : match.get_results()) Challenge::start(*connection, settings.challenge_username, key);
{
std::cout << " " << std::string_view{c.first, c.second} << std::endl;
}
}); });
*/
client->sig_registered.connect([&settings, connection, client]() {
connection->send_join("##glguy"sv);
connection->send_whois(client->get_my_nick());
if (not settings.challenge_username.empty() &&
not settings.challenge_key_file.empty()) {
auto key = key_from_file(settings.challenge_key_file, settings.challenge_key_password);
Challenge::start(*connection, settings.challenge_username, std::move(key));
} }
}); }
client->sig_chat.connect([client, connection](const Chat &chat){
if (chat.source.starts_with("glguy!") && client->is_my_nick(chat.target)) {
connection->send_notice("glguy", chat.message);
}});
connection->sig_disconnect.connect( connection->sig_disconnect.connect(
[&io, &settings, client, bot]() { [&io, &settings, client, bot]() {

View File

@ -1,14 +0,0 @@
#include "openssl_errors.hpp"
#include "c_callback.hpp"
#include <boost/log/trivial.hpp>
auto log_openssl_errors(const std::string_view prefix) -> void
{
auto err_cb = [prefix](const char *str, size_t len) -> int {
BOOST_LOG_TRIVIAL(error) << prefix << std::string_view{str, len};
return 0;
};
ERR_print_errors_cb(CCallback<decltype(err_cb)>::invoke, &err_cb);
}

View File

@ -1,7 +0,0 @@
#pragma once
#include <openssl/err.h>
#include <string_view>
auto log_openssl_errors(const std::string_view prefix) -> void;

42
ref.hpp
View File

@ -5,14 +5,40 @@
#include <memory> #include <memory>
template <typename T, void Free(T*)> template <typename> struct RefTraits {};
struct FnDeleter {
auto operator()(T *ptr) const -> void { Free(ptr); } template <> struct RefTraits<EVP_PKEY> {
static constexpr void (*Free)(EVP_PKEY*) = EVP_PKEY_free;
static constexpr int (*UpRef)(EVP_PKEY*) = EVP_PKEY_up_ref;
}; };
template <typename T, void(Free)(T*)> template <> struct RefTraits<X509> {
using Ref = std::unique_ptr<T, FnDeleter<T, Free>>; static constexpr void (*Free)(X509*) = X509_free;
static constexpr int (*UpRef)(X509*) = X509_up_ref;
};
using EVP_PKEY_CTX_Ref = Ref<EVP_PKEY_CTX, EVP_PKEY_CTX_free>; template <> struct RefTraits<EVP_PKEY_CTX> {
using X509_Ref = Ref<X509, X509_free>; static constexpr void (*Free)(EVP_PKEY_CTX*) = EVP_PKEY_CTX_free;
using EVP_PKEY_Ref = Ref<EVP_PKEY, EVP_PKEY_free>; };
template <typename T>
struct FnDeleter {
auto operator()(T *ptr) const -> void { RefTraits<T>::Free(ptr); }
};
template <typename T>
struct Ref : std::unique_ptr<T, FnDeleter<T>> {
using std::unique_ptr<T, FnDeleter<T>>::unique_ptr;
Ref(const Ref &ref) {
*this = ref;
}
Ref &operator=(const Ref &ref) {
RefTraits<T>::UpRef(ref.get());
this->reset(ref.get());
return *this;
}
};
using EVP_PKEY_CTX_Ref = Ref<EVP_PKEY_CTX>;
using X509_Ref = Ref<X509>;
using EVP_PKEY_Ref = Ref<EVP_PKEY>;