diff options
-rw-r--r-- | data/modules.example.conf | 17 | ||||
-rw-r--r-- | modules/sql_authentication.cpp | 43 |
2 files changed, 56 insertions, 4 deletions
diff --git a/data/modules.example.conf b/data/modules.example.conf index a242c0a6d..58c167c86 100644 --- a/data/modules.example.conf +++ b/data/modules.example.conf @@ -671,6 +671,23 @@ module query = "SELECT `email_addr` AS `email` FROM `my_users` WHERE `username` = @a@ AND `password` = MD5(CONCAT('salt', @p@))" /* + * If your database uses a password hashing algorithm that can not be compared using a simple + * comparison function then you can specify it here to compare locally. + * + * You will need to have the appropriate encryption module (e.g. enc_bcrypt) loaded in order + * for this to work. + */ + #password_hash = "bcrypt" + + /* + * If using the password_hash field (above) you will need to specify the name of the field to + * fetch the password from. + * + * Defaults to "password" if not set. + */ + #password_field = "password" + + /* * If set, the reason to give the users who try to "/msg NickServ REGISTER". * If not set, then registration is not blocked. */ diff --git a/modules/sql_authentication.cpp b/modules/sql_authentication.cpp index a524a43fc..9c3605ed9 100644 --- a/modules/sql_authentication.cpp +++ b/modules/sql_authentication.cpp @@ -7,16 +7,29 @@ */ #include "module.h" +#include "modules/encryption.h" #include "modules/sql.h" -static Module *me; +namespace +{ + Module *me; + ServiceReference<Encryption::Provider> encryption; + Anope::string password_hash, password_field; +} class SQLAuthenticationResult final : public SQL::Interface { Reference<User> user; IdentifyRequest *req; + void OnFail(const Anope::string &reason) + { + Log(LOG_DEBUG) << "sql_authentication: Unsuccessful authentication for " << req->GetAccount() << ": " << reason; + delete this; + return; + } + public: SQLAuthenticationResult(User *u, IdentifyRequest *r) : SQL::Interface(me), user(u), req(r) { @@ -31,10 +44,25 @@ public: void OnResult(const SQL::Result &r) override { if (r.Rows() == 0) + return OnFail("no rows"); + + if (!password_hash.empty()) { - Log(LOG_DEBUG) << "sql_authentication: Unsuccessful authentication for " << req->GetAccount(); - delete this; - return; + if (!encryption) + return OnFail("encryption provider not loaded"); + + auto success = false; + for (auto i = 0; i < r.Rows(); ++i) + { + if (encryption->Compare(r.Get(i, password_field), req->GetPassword())) + { + success = true; + break; + } + } + + if (!success) + return OnFail("no matching passwords"); } Log(LOG_DEBUG) << "sql_authentication: Successful authentication for " << req->GetAccount(); @@ -99,6 +127,13 @@ public: this->disable_email_reason = config.Get<Anope::string>("disable_email_reason"); this->SQL = ServiceReference<SQL::Provider>("SQL::Provider", this->engine); + + password_hash = config.Get<const Anope::string>("password_hash"); + if (!password_hash.empty()) + { + password_field = config.Get<const Anope::string>("password_field", "password"); + encryption = ServiceReference<Encryption::Provider>("Encryption::Provider", password_hash); + } } EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> ¶ms) override |