summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSadie Powell <sadie@witchery.services>2025-05-04 14:39:20 +0100
committerSadie Powell <sadie@witchery.services>2025-05-04 14:39:20 +0100
commit72f5e3580fdc68161642ee0f11a354dbc74fffeb (patch)
treee1bd6f46e8a76fe8194425e1af03e421bf2faafe
parent4b854d39357cbcfa43eb833949d48a49b0420ad6 (diff)
Also allow hashed RPC tokens in the config file.
-rw-r--r--data/modules.example.conf20
-rw-r--r--include/modules/rpc.h49
-rw-r--r--modules/extra/xmlrpc.cpp12
-rw-r--r--modules/rpc/jsonrpc.cpp12
4 files changed, 70 insertions, 23 deletions
diff --git a/data/modules.example.conf b/data/modules.example.conf
index bb9e6f57a..243f77e09 100644
--- a/data/modules.example.conf
+++ b/data/modules.example.conf
@@ -672,7 +672,7 @@ module
/*
* If your database uses a password hashing algorithm that can not be compared using a simple
- * comparison method then you can specify it here to compare locally.
+ * 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.
@@ -820,6 +820,15 @@ module
/* The token used for authentication. */
token = "BmcxTaiYjoBtayfnxCFq"
+ /*
+ * The algorithm which the above token is hashed with. If this is not
+ * set then services will assume the above password is not hashed.
+ *
+ * You will need to have the appropriate encryption module (e.g.
+ * enc_bcrypt) loaded in order for this to work.
+ */
+ #token_hash = "bcrypt"
+
/** A list of glob patterns for methods the token can execute. */
methods = "~anope.message* anope.*"
}
@@ -867,6 +876,15 @@ module
/* The token used for authentication. */
token = "BmcxTaiYjoBtayfnxCFq"
+ /*
+ * The algorithm which the above token is hashed with. If this is not
+ * set then services will assume the above password is not hashed.
+ *
+ * You will need to have the appropriate encryption module (e.g.
+ * enc_bcrypt) loaded in order for this to work.
+ */
+ #token_hash = "bcrypt"
+
/** A list of glob patterns for methods the token can execute. */
methods = "~anope.message* anope.*"
}
diff --git a/include/modules/rpc.h b/include/modules/rpc.h
index 41a95fc14..1c3dec811 100644
--- a/include/modules/rpc.h
+++ b/include/modules/rpc.h
@@ -8,6 +8,7 @@
#pragma once
+#include "encryption.h"
#include "httpd.h"
#include <variant>
@@ -19,6 +20,7 @@ namespace RPC
class Map;
class Request;
class ServiceInterface;
+ struct Token;
class Value;
/** Represents possible types of RPC value. */
@@ -189,11 +191,32 @@ public:
virtual bool Run(ServiceInterface *iface, HTTPClient *client, Request &request) = 0;
};
+struct RPC::Token final
+{
+ std::vector<Anope::string> methods;
+ Anope::string token;
+ Anope::string token_hash;
+};
+
class RPC::ServiceInterface
: public Service
{
+private:
+ bool CompareToken(const RPC::Token &token, const Anope::string &rawtoken) const
+ {
+ if (token.token_hash.empty())
+ return token.token.equals_cs(rawtoken); // Plaintext token.
+
+ auto *service = Service::FindService("Encryption::Provider", token.token_hash);
+ if (!service)
+ return false; // Malformed hash.
+
+ auto *hashprov = static_cast<Encryption::Provider *>(service);
+ return hashprov->Compare(token.token, rawtoken);
+ }
+
public:
- Anope::map<std::vector<Anope::string>> tokens;
+ std::vector<Token> tokens;
ServiceInterface(Module *creator)
: Service(creator, "RPC::ServiceInterface", "rpc")
@@ -205,20 +228,22 @@ public:
if (header.compare(0, 7, "Bearer ", 7) != 0)
return false; // No token provided.
- Anope::string token;
- Anope::B64Decode(header.substr(7), token);
+ Anope::string rawtoken;
+ Anope::B64Decode(header.substr(7), rawtoken);
- auto it = tokens.find(token);
- if (it == tokens.end())
- return false; // No valid token.
-
- for (const auto &glob : it->second)
+ for (const auto &token : tokens)
{
- if (glob[0] == '~' && Anope::Match(method, glob.substr(1)))
- return false; // Negative match.
+ if (!CompareToken(token, rawtoken))
+ continue; // No valid token.
+
+ for (const auto &glob : token.methods)
+ {
+ if (glob[0] == '~' && Anope::Match(method, glob.substr(1)))
+ return false; // Negative match.
- if (Anope::Match(method, glob))
- return true; // Positive match.
+ if (Anope::Match(method, glob))
+ return true; // Positive match.
+ }
}
return false; // No match.
}
diff --git a/modules/extra/xmlrpc.cpp b/modules/extra/xmlrpc.cpp
index 24f5f5f0e..3e6a8af76 100644
--- a/modules/extra/xmlrpc.cpp
+++ b/modules/extra/xmlrpc.cpp
@@ -324,12 +324,14 @@ public:
for (int i = 0; i < modconf.CountBlock("token"); ++i)
{
const auto &block = modconf.GetBlock("token", i);
- const auto &token = block.Get<const Anope::string>("token");
- if (!token.empty())
+
+ RPC::Token token;
+ token.token = block.Get<const Anope::string>("token");
+ if (!token.token.empty())
{
- std::vector<Anope::string> methods;
- spacesepstream(block.Get<const Anope::string>("methods")).GetTokens(methods);
- xmlrpcinterface.tokens.emplace(token, methods);
+ token.token_hash = block.Get<const Anope::string>("token_hash");
+ spacesepstream(block.Get<const Anope::string>("methods")).GetTokens(token.methods);
+ xmlrpcinterface.tokens.emplace_back(token);
}
}
diff --git a/modules/rpc/jsonrpc.cpp b/modules/rpc/jsonrpc.cpp
index 8069ed8eb..163658495 100644
--- a/modules/rpc/jsonrpc.cpp
+++ b/modules/rpc/jsonrpc.cpp
@@ -281,12 +281,14 @@ public:
for (int i = 0; i < modconf.CountBlock("token"); ++i)
{
const auto &block = modconf.GetBlock("token", i);
- const auto &token = block.Get<const Anope::string>("token");
- if (!token.empty())
+
+ RPC::Token token;
+ token.token = block.Get<const Anope::string>("token");
+ if (!token.token.empty())
{
- std::vector<Anope::string> methods;
- spacesepstream(block.Get<const Anope::string>("methods")).GetTokens(methods);
- jsonrpcinterface.tokens.emplace(token, methods);
+ token.token_hash = block.Get<const Anope::string>("token_hash");
+ spacesepstream(block.Get<const Anope::string>("methods")).GetTokens(token.methods);
+ jsonrpcinterface.tokens.emplace_back(token);
}
}