fixup challenge

This commit is contained in:
Eric Mertens 2025-01-28 20:01:51 -08:00
parent 72b2756f34
commit 1aa56453cc
10 changed files with 116 additions and 38 deletions

View File

@ -46,7 +46,6 @@ add_executable(xbot
challenge.cpp challenge.cpp
client.cpp client.cpp
connection.cpp connection.cpp
irc_coroutine.cpp
ircmsg.cpp ircmsg.cpp
openssl_errors.cpp openssl_errors.cpp
registration.cpp registration.cpp

View File

@ -39,9 +39,11 @@ auto Challenge::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void {
break; break;
case IrcCommand::RPL_ENDOFRSACHALLENGE2: case IrcCommand::RPL_ENDOFRSACHALLENGE2:
{ {
slot_.disconnect();
EVP_PKEY_CTX_Ref ctx; EVP_PKEY_CTX_Ref ctx;
unsigned int digestlen = 160; unsigned int digestlen = EVP_MAX_MD_SIZE;
unsigned char digest[160]; 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);
@ -52,14 +54,15 @@ auto Challenge::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void {
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) goto error;
if (1 != EVP_PKEY_decrypt_init(ctx.get())) goto error; if (1 != EVP_PKEY_decrypt_init(ctx.get())) goto error;
if (0 <= EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING)) goto error; if (0 >= EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING)) goto error;
// 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())) goto error;
buffer_.resize(len); buffer_.resize(len);
// Decrypt ciphertext // Decrypt ciphertext
EVP_PKEY_decrypt(ctx.get(), reinterpret_cast<unsigned char*>(buffer_.data()), &len, ciphertext.data(), ciphertext.size()); if (1 != EVP_PKEY_decrypt(ctx.get(), reinterpret_cast<unsigned char*>(buffer_.data()), &len, ciphertext.data(), ciphertext.size())) goto error;
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)) goto error;
@ -70,13 +73,10 @@ 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_);
stop();
return; return;
error: error:
log_openssl_errors("Challenge: "); log_openssl_errors("Challenge: ");
stop();
} }
} }
} }

View File

@ -1,20 +0,0 @@
#include "irc_coroutine.hpp"
auto irc_coroutine::is_running() -> bool
{
return promise().connection_ != nullptr;
}
auto irc_coroutine::exception() -> std::exception_ptr
{
return promise().exception_;
}
auto irc_coroutine::start(Connection &connection) -> void
{
promise().connection_ = connection.shared_from_this();
resume();
}
void wait_ircmsg::stop() { ircmsg_slot_.disconnect(); }
void wait_timeout::stop() { timer_.reset(); }

View File

@ -80,7 +80,28 @@ public:
template <size_t I, typename... Ts> template <size_t I, typename... Ts>
auto start(Wait<Ts...> &command) -> void; auto start(Wait<Ts...> &command) -> void;
auto stop() -> void; auto stop() -> void { ircmsg_slot_.disconnect(); }
};
class wait_snote
{
// Vector of tags this wait is expecting. Leave empty to accept all messages.
std::vector<SnoteTag> want_tags_;
// Slot for the snote event
boost::signals2::scoped_connection snote_slot_;
public:
using result_type = SnoteMatch;
wait_snote(std::initializer_list<SnoteTag> want_tags)
: want_tags_{want_tags}
{
}
template <size_t I, typename... Ts>
auto start(Wait<Ts...> &command) -> void;
auto stop() -> void { snote_slot_.disconnect(); }
}; };
class wait_timeout class wait_timeout
@ -99,7 +120,7 @@ public:
template <size_t I, typename... Ts> template <size_t I, typename... Ts>
auto start(Wait<Ts...> &command) -> void; auto start(Wait<Ts...> &command) -> void;
auto stop() -> void; auto stop() -> void { timer_->cancel(); }
}; };
template <typename... Ts> template <typename... Ts>
@ -180,6 +201,19 @@ auto wait_ircmsg::start(Wait<Ts...> &command) -> void
}); });
} }
template <size_t I, typename... Ts>
auto wait_snote::start(Wait<Ts...> &command) -> void
{
snote_slot_ = command.get_connection().sig_snote.connect([this, &command](auto &match) {
const auto tag = match.get_tag();
const auto wanted = want_tags_.empty() || std::find(want_tags_.begin(), want_tags_.end(), tag) != want_tags_.end();
if (wanted)
{
command.template complete<I>(match);
}
});
}
template <size_t I, typename... Ts> template <size_t I, typename... Ts>
auto wait_timeout::start(Wait<Ts...> &command) -> void auto wait_timeout::start(Wait<Ts...> &command) -> void
{ {
@ -224,3 +258,21 @@ auto Wait<Ts...>::await_resume() -> std::variant<typename Ts::result_type...>
throw std::runtime_error{"connection terminated"}; throw std::runtime_error{"connection terminated"};
} }
} }
/// Start the coroutine and associate it with a specific connection.
inline auto irc_coroutine::start(Connection &connection) -> void
{
promise().connection_ = connection.shared_from_this();
resume();
}
/// Returns true when this coroutine is still waiting on events
inline auto irc_coroutine::is_running() -> bool
{
return promise().connection_ != nullptr;
}
inline auto irc_coroutine::exception() -> std::exception_ptr
{
return promise().exception_;
}

View File

@ -6,6 +6,7 @@
#include "openssl_errors.hpp" #include "openssl_errors.hpp"
#include "registration.hpp" #include "registration.hpp"
#include "settings.hpp" #include "settings.hpp"
#include "irc_coroutine.hpp"
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
@ -130,22 +131,26 @@ static auto start(boost::asio::io_context &io, const Settings &settings) -> void
}); });
} }
static auto get_settings() -> Settings static auto get_settings(const char *filename) -> Settings
{ {
if (auto config_stream = std::ifstream{"config.toml"}) if (auto config_stream = std::ifstream{filename})
{ {
return Settings::from_stream(config_stream); return Settings::from_stream(config_stream);
} }
else else
{ {
BOOST_LOG_TRIVIAL(error) << "Unable to open config.toml"; BOOST_LOG_TRIVIAL(error) << "Unable to open configuration";
std::exit(1); std::exit(1);
} }
} }
auto main() -> int auto main(int argc, char *argv[]) -> int
{ {
const auto settings = get_settings(); if (argc != 2) {
BOOST_LOG_TRIVIAL(error) << "Bad arguments";
return 1;
}
const auto settings = get_settings(argv[1]);
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
start(io, settings); start(io, settings);
io.run(); io.run();

View File

@ -81,12 +81,14 @@ auto Registration::on_cap_list(const std::unordered_map<std::string, std::string
client_->get_connection().send_cap_req(request); client_->get_connection().send_cap_req(request);
} }
if (do_sasl) { if (do_sasl && settings_.sasl_mechanism == "PLAIN") {
client_->start_sasl( client_->start_sasl(
std::make_unique<SaslPlain>( std::make_unique<SaslPlain>(
settings_.sasl_authcid, settings_.sasl_authcid,
settings_.sasl_authzid, settings_.sasl_authzid,
settings_.sasl_password)); settings_.sasl_password));
} else if (do_sasl && settings_.sasl_mechanism == "EXTERNAL") {
client_->start_sasl(std::make_unique<SaslExternal>(settings_.sasl_authzid));
} else { } else {
client_->get_connection().send_cap_end(); client_->get_connection().send_cap_end();
} }

View File

@ -17,3 +17,11 @@ auto SaslPlain::step(std::string_view msg) -> std::optional<std::string> {
return {std::move(reply)}; return {std::move(reply)};
} }
} }
auto SaslExternal::step(std::string_view msg) -> std::optional<std::string> {
if (complete_) {
return std::nullopt;
} else {
return {std::move(authzid_)};
}
}

View File

@ -44,3 +44,27 @@ public:
return complete_; return complete_;
} }
}; };
class SaslExternal final : public SaslMechanism
{
std::string authzid_;
bool complete_;
public:
SaslExternal(std::string authzid)
: authzid_{std::move(authzid)}
, complete_{false}
{}
auto mechanism_name() const -> std::string override
{
return "EXTERNAL";
}
auto step(std::string_view msg) -> std::optional<std::string> override;
auto is_complete() const -> bool override
{
return complete_;
}
};

View File

@ -74,6 +74,12 @@ const SnotePattern static patterns[] = {
{SnoteTag::SetVhostOnMarkedAccount, {SnoteTag::SetVhostOnMarkedAccount,
"^\x02([^ ]+)\x02 set vhost ([^ ]+) on the \x02MARKED\x02 account ([^ ]+).$"}, "^\x02([^ ]+)\x02 set vhost ([^ ]+) on the \x02MARKED\x02 account ([^ ]+).$"},
{SnoteTag::IsNowOper,
R"(^([^ ]+) \(([^ ]+)!([^ ]+)@([^ ]+)\) is now an operator$)"},
{SnoteTag::NickCollision,
R"(^Nick collision due to services forced nick change on ([^ ]+)$)"},
}; };
static auto setup_database() -> hs_database_t * static auto setup_database() -> hs_database_t *

View File

@ -29,6 +29,8 @@ enum class SnoteTag
Killed, Killed,
TooManyGlobalConnections, TooManyGlobalConnections,
SetVhostOnMarkedAccount, SetVhostOnMarkedAccount,
IsNowOper,
NickCollision,
}; };
class SnoteMatch class SnoteMatch