From 2e1ddeb6547e140e9651231fedcd00e8ee4b1ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 14 Jun 2020 22:54:42 +0200 Subject: Implement SASL plain authentication --- src/irc/irc_client.cpp | 118 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 13 deletions(-) (limited to 'src/irc/irc_client.cpp') diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index de38d42..183a9d8 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -81,6 +82,11 @@ static const std::unordered_mapsend_message({"CAP", {"REQ", "multi-prefix"}}); - this->send_message({"CAP", {"END"}}); + this->send_gateway_message("Connected to IRC server"s + (this->use_tls ? " (encrypted)": "") + "."); + + this->capabilities["multi-prefix"] = {[]{}, []{}}; #ifdef USE_DATABASE auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), this->get_hostname()); - if (!options.col().empty()) + + const auto& sasl_password = options.col(); + const auto& server_password = options.col(); + + if (!server_password.empty()) this->send_pass_command(options.col()); + + if (!sasl_password.empty()) + { + this->capabilities["sasl"] = { + [this] + { + this->send_message({"AUTHENTICATE", {"PLAIN"}}); + log_warning("negociating SASL now..."); + }, + [] + { + log_warning("SASL not supported by the server, disconnecting."); + } + }; + this->sasl_state = SaslState::needed; + } #endif - this->send_nick_command(this->current_nick); + { + for (const auto &pair : this->capabilities) + this->send_message({ "CAP", {"REQ", pair.first}}); + } + this->send_nick_command(this->current_nick); #ifdef USE_DATABASE if (Config::get("realname_customization", "true") == "true") { @@ -298,9 +329,6 @@ void IrcClient::on_connected() #else this->send_user_command(this->username, this->realname); #endif - this->send_gateway_message("Connected to IRC server"s + (this->use_tls ? " (encrypted)": "") + "."); - this->send_pending_data(); - this->bridge.on_irc_client_connected(this->get_hostname()); } void IrcClient::on_connection_close(const std::string& error_msg) @@ -371,12 +399,14 @@ void IrcClient::parse_in_buffer(const size_t) { const auto& limits = it->second.second; // Check that the Message is well formed before actually calling - // the callback. limits.first is the min number of arguments, - // second is the max - if (message.arguments.size() < limits.first || - (limits.second > 0 && message.arguments.size() > limits.second)) + // the callback. + const auto args_size = message.arguments.size(); + const auto min = limits.first; + const auto max = limits.second; + if (args_size < min || + (max > 0 && args_size > max)) log_warning("Invalid number of arguments for IRC command “", message.command, - "”: ", message.arguments.size()); + "”: ", args_size); else { const auto& cb = it->second.first; @@ -1266,7 +1296,7 @@ void IrcClient::on_unknown_message(const IrcMessage& message) return ; std::string from = message.prefix; std::stringstream ss; - for (auto it = message.arguments.begin() + 1; it != message.arguments.end(); ++it) + for (auto it = std::next(message.arguments.begin()); it != message.arguments.end(); ++it) { ss << *it; if (it + 1 != message.arguments.end()) @@ -1299,3 +1329,65 @@ long int IrcClient::get_throttle_limit() const return 10; #endif } + +void IrcClient::on_cap(const IrcMessage &message) +{ + const auto& sub_command = message.arguments[1]; + const auto& cap = message.arguments[2]; + auto it = this->capabilities.find(cap); + if (it == this->capabilities.end()) + { + log_warning("Received a CAP message for something we didn’t ask, or that we already handled."); + return; + } + Capability& capability = it->second; + if (sub_command == "ACK") + capability.on_ack(); + else if (sub_command == "NACK") + capability.on_nack(); + this->capabilities.erase(it); + this->cap_end(); +} + +void IrcClient::on_authenticate(const IrcMessage &) +{ + if (this->sasl_state == SaslState::unneeded) + { + log_warning("Received an AUTHENTICATE command but we don’t intend to authenticate…"); + return; + } +#ifdef USE_DATABASE + auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), + this->get_hostname()); + const auto auth_string = '\0' + options.col() + '\0' + options.col(); + const auto base64_auth_string = base64::encode(auth_string); + this->send_message({"AUTHENTICATE", {base64_auth_string}}); +#endif +} + +void IrcClient::on_sasl_success(const IrcMessage &) +{ + this->sasl_state = SaslState::success; + this->cap_end(); +} + +void IrcClient::on_sasl_login(const IrcMessage &message) +{ + const auto& login = message.arguments[2]; + std::string text = "Your are now logged in as " + login; + if (message.arguments.size() > 3) + text = message.arguments[3]; + this->bridge.send_xmpp_message(this->hostname, message.prefix, text); +} + +void IrcClient::cap_end() +{ + if (!this->capabilities.empty()) + return; + // If we are currently authenticating through sasl, finish that before sending CAP END + if (this->sasl_state == SaslState::needed) + return; + + this->send_message({"CAP", {"END"}}); + this->bridge.on_irc_client_connected(this->get_hostname()); +} -- cgit v1.2.3