From 72f5e3580fdc68161642ee0f11a354dbc74fffeb Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Sun, 4 May 2025 14:39:20 +0100 Subject: [PATCH] Also allow hashed RPC tokens in the config file. --- data/modules.example.conf | 20 +++++++++++++++- include/modules/rpc.h | 49 +++++++++++++++++++++++++++++---------- modules/extra/xmlrpc.cpp | 12 ++++++---- 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 @@ -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 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(service); + return hashprov->Compare(token.token, rawtoken); + } + public: - Anope::map> tokens; + std::vector 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. - if (Anope::Match(method, glob)) - return true; // Positive match. + 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. + } } 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("token"); - if (!token.empty()) + + RPC::Token token; + token.token = block.Get("token"); + if (!token.token.empty()) { - std::vector methods; - spacesepstream(block.Get("methods")).GetTokens(methods); - xmlrpcinterface.tokens.emplace(token, methods); + token.token_hash = block.Get("token_hash"); + spacesepstream(block.Get("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("token"); - if (!token.empty()) + + RPC::Token token; + token.token = block.Get("token"); + if (!token.token.empty()) { - std::vector methods; - spacesepstream(block.Get("methods")).GetTokens(methods); - jsonrpcinterface.tokens.emplace(token, methods); + token.token_hash = block.Get("token_hash"); + spacesepstream(block.Get("methods")).GetTokens(token.methods); + jsonrpcinterface.tokens.emplace_back(token); } }