/* * asbots * Copyright (C) 2021 niansa * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "nickserv.hpp" #include "../instance.hpp" #include #include #include #include #include #include #include #include #include using fmt::operator""_format; async::result NickServ::intitialize() { co_await i->netInfo.wait_ready(); user = { .server = i->config.server.uid, .nick = std::string(getConfig("NickServ", "nickname").value_or("NickServ")), .realhost = i->config.server.name, .uid = uuid, .realname = i->netInfo.name }; co_await mark_ready(user); using namespace sqlite_orm; storage = std::make_unique(make_storage( std::string(getConfig("NickServ", "database").value_or("nickserv.sqlite")), make_table("users", make_column("id", &Account::id, autoincrement(), primary_key()), make_column("name", &Account::name), make_column("email", &Account::email), make_column("password_hash", &Account::password_hash)) )); storage->sync_schema(); commands = { {"identify", {[this] (u_User& sender, const std::vector& args) -> async::result { // Check that user is not already identified if (sender->loginName.has_value()) { co_await i->send_event(user.get_notice("Error: You've already been identified!", sender->uid)); co_return; } // Get account name and password to use std::string_view accountName, accountPwd; if (args.size() > 1) { accountName = args[0]; accountPwd = args[1]; } else { accountName = sender->nick; accountPwd = args[0]; } // Get account from storage auto accounts_found = storage->get_all(where(c(&Account::name) == std::string(accountName))); if (accounts_found.empty()) { co_await i->send_event(user.get_notice("Error: {} is not registered!"_format(accountName), sender->uid)); co_return; } auto& account = accounts_found[0]; // Check password if (account.password_hash != pwdHash(accountPwd)) { co_await i->send_event(user.get_notice("Error: Invalid password!", sender->uid)); co_return; } // Login co_await i->send_event(sender->get_encap_su(account.name, i->config.server.uid)); // Report success co_await i->send_event(user.get_notice("You have been identified!", sender->uid)); }, "[username] ", 1, false} }, {"deidentify", {[this] (u_User& sender, const std::vector& args) -> async::result { co_await i->send_event(sender->get_encap_su("", i->config.server.uid)); co_await i->send_event(user.get_notice("You have been deidentified!", sender->uid)); }, "", 0, true} }, {"register", {[this] (u_User& sender, std::vector args) -> async::result { // Check args if (args.size() != 2) { co_await i->send_event(user.get_notice("Error: Passwords may not contain spaces!", sender->uid)); co_return; } // Check that account does not exist already { auto accounts_found = storage->get_all(where(c(&Account::name) == sender->nick)); if (!accounts_found.empty()) { co_await i->send_event(user.get_notice("Error: {} has already been registered!"_format(sender->nick), sender->uid)); co_return; } } // Insert new account into database storage->insert(Account{ .name = sender->nick, .email = std::string(args[1]), .password_hash = pwdHash(args[0]) }); // Report success co_await i->send_event(user.get_notice("{} has been registered!"_format(sender->nick), sender->uid)); }, " ", 2, false} }, }; } std::string NickServ::pwdHash(std::string_view pwd) { using namespace CryptoPP; auto salt = getConfig("NickServ", "salt").value_or("defaultsalt"); static_assert(sizeof(pwd[0]) == 1, "std::string_view character width must be 1 for this to work... You might have a bad STL implementation."); int c = 1; byte derived[20]; PKCS5_PBKDF2_HMAC pbkdf2; pbkdf2.DeriveKey(derived, sizeof(derived), 0, reinterpret_cast(pwd.data()), pwd.size(), reinterpret_cast(salt.data()), salt.size(), c); std::string fres; HexEncoder encoder(new StringSink(fres)); encoder.Put(derived, sizeof(derived)); encoder.MessageEnd(); return fres; }