summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSadie Powell <sadie@witchery.services>2025-04-11 12:05:18 +0100
committerSadie Powell <sadie@witchery.services>2025-04-19 12:38:32 +0100
commit452e62c05021b29b4f0641245cafa6ae115e2198 (patch)
treee5bf110189f06b7c7afaa8e58ed3b2b03e5220be
parent4916e1dbfcbea01830d16b27ec90930c875bc335 (diff)
Add support for local password comparison in sql_authentication.
-rw-r--r--data/modules.example.conf17
-rw-r--r--modules/sql_authentication.cpp43
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> &params) override