diff options
author | Sadie Powell <sadie@witchery.services> | 2025-05-04 14:39:20 +0100 |
---|---|---|
committer | Sadie Powell <sadie@witchery.services> | 2025-05-04 14:39:20 +0100 |
commit | 72f5e3580fdc68161642ee0f11a354dbc74fffeb (patch) | |
tree | e1bd6f46e8a76fe8194425e1af03e421bf2faafe | |
parent | 4b854d39357cbcfa43eb833949d48a49b0420ad6 (diff) |
Also allow hashed RPC tokens in the config file.
-rw-r--r-- | data/modules.example.conf | 20 | ||||
-rw-r--r-- | include/modules/rpc.h | 49 | ||||
-rw-r--r-- | modules/extra/xmlrpc.cpp | 12 | ||||
-rw-r--r-- | modules/rpc/jsonrpc.cpp | 12 |
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); } } |