Address review feedback: explicit mark_safe whitelist and rpc trust flow

This commit is contained in:
Xirvik
2026-03-03 04:32:54 +00:00
committed by Jari Sundell
parent ba239bc8c5
commit d935e0ffe9
17 changed files with 807 additions and 560 deletions
+2 -2
View File
@@ -98,7 +98,7 @@ CommandMap::call_command(const key_type& key, const mapped_type& arg, const targ
if (itr == base_type::end())
throw torrent::input_error("Command \"" + std::string(key) + "\" does not exist.");
if (!RpcManager::is_trusted() && !(itr->second.m_flags & flag_untrusted_safe))
if (!rpc.is_trusted() && !(itr->second.m_flags & flag_untrusted_safe))
throw untrusted_error("Command \"" + std::string(key) + "\" is not allowed for untrusted connections.");
return itr->second.m_anySlot(&itr->second.m_variable, target, arg);
@@ -106,7 +106,7 @@ CommandMap::call_command(const key_type& key, const mapped_type& arg, const targ
const CommandMap::mapped_type
CommandMap::call_command(iterator itr, const mapped_type& arg, const target_type& target) {
if (!RpcManager::is_trusted() && !(itr->second.m_flags & flag_untrusted_safe))
if (!rpc.is_trusted() && !(itr->second.m_flags & flag_untrusted_safe))
throw untrusted_error("Command \"" + itr->first + "\" is not allowed for untrusted connections.");
return itr->second.m_anySlot(&itr->second.m_variable, target, arg);
+25 -3
View File
@@ -21,8 +21,6 @@ ExecFile execFile;
// CommandMap::call_command(), which catches all command execution
// including nested calls through argument expansion.
thread_local bool RpcManager::m_trusted = true;
bool
RpcManager::set_trusted(bool trusted) {
bool prev = m_trusted;
@@ -31,7 +29,7 @@ RpcManager::set_trusted(bool trusted) {
}
bool
RpcManager::is_trusted() {
RpcManager::is_trusted() const {
return m_trusted;
}
@@ -146,6 +144,20 @@ RpcManager::process(RPCType type, const char* in_buffer, uint32_t length, slot_r
}
}
bool
RpcManager::process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) {
bool previous = set_trusted(false);
try {
bool result = process(type, in_buffer, length, callback);
set_trusted(previous);
return result;
} catch (...) {
set_trusted(previous);
throw;
}
}
void
RpcManager::initialize_handlers() {
if (m_handlers_initialized)
@@ -197,4 +209,14 @@ RpcManager::insert_command(const char* name, const char* parm, const char* doc)
m_jsonrpc.insert_command(name, parm, doc);
}
void
RpcManager::mark_safe(const std::string& key) {
auto itr = commands.find(key);
if (itr == commands.end())
return;
itr->second.m_flags |= CommandMap::flag_untrusted_safe;
}
} // namespace rpc
+8 -10
View File
@@ -4,6 +4,7 @@
#include <cstdint>
#include <functional>
#include <torrent/common.h>
#include <torrent/exceptions.h>
#include "rpc/command.h"
#include "rpc/command_map.h"
@@ -34,15 +35,10 @@ private:
std::string m_msg;
};
class untrusted_error : public torrent::base_error {
class untrusted_error : public torrent::input_error {
public:
untrusted_error(std::string msg) : m_msg(std::move(msg)) {}
using torrent::input_error::input_error;
virtual ~untrusted_error() throw() = default;
virtual const char* what() const throw() { return m_msg.c_str(); }
private:
std::string m_msg;
};
class RpcManager {
@@ -74,8 +70,10 @@ public:
void set_type_enabled(RPCType type, bool enabled);
bool process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback);
bool process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback);
void insert_command(const char* name, const char* parm, const char* doc);
void mark_safe(const std::string& key);
slot_download& slot_find_download() { return m_slot_find_download; }
slot_file& slot_find_file() { return m_slot_find_file; }
@@ -85,13 +83,13 @@ public:
// Trusted/untrusted XMLRPC connection model.
// When an SCGI request includes the UNTRUSTED_CONNECTION header,
// commands without flag_untrusted_safe are blocked.
static bool set_trusted(bool trusted);
static bool is_trusted();
bool set_trusted(bool trusted);
bool is_trusted() const;
static void object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target, std::function<void()>* deleter);
private:
static thread_local bool m_trusted;
bool m_trusted{true};
XmlRpc m_xmlrpc;
JsonRpc m_jsonrpc;
+8 -13
View File
@@ -305,20 +305,15 @@ SCgiTask::receive_call(const char* buffer, uint32_t length) {
}
torrent::main_thread::thread()->callback_interrupt_polling(this, [buffer, length, result_callback, trusted, rpc_type]() {
rpc::RpcManager::set_trusted(trusted);
auto callback = [result_callback](const char* b, uint32_t l) {
result_callback(b, l);
return true;
};
try {
rpc.process(rpc_type, buffer, length,
[result_callback](const char* b, uint32_t l) {
result_callback(b, l);
return true;
});
} catch (...) {
rpc::RpcManager::set_trusted(true);
throw;
}
rpc::RpcManager::set_trusted(true);
if (trusted)
rpc.process(rpc_type, buffer, length, callback);
else
rpc.process_untrusted(rpc_type, buffer, length, callback);
});
}