Add untrusted connection security infrastructure (v3)

Replace the v2 blacklist approach with a per-command flag system.
Commands must opt in to being available for untrusted connections
via flag_untrusted_safe (0x400), checked in call_command() which
catches all execution paths including nested commands.

Infrastructure changes:
- Add flag_untrusted_safe to CommandMap
- Add untrusted_error exception type for proper error codes
- Enforce trust check in both call_command() overloads
- Add catch blocks in xmlrpc_c, xmlrpc_tinyxml2, and jsonrpc handlers
- Port SCGI trust state management from v2 (thread_local, header parsing)
- Add _U macro variants in command_helpers.h for safe command registration
- Add CMD2_VAR_*_U and CMD2_VAR_*_U_GET variants for variables
This commit is contained in:
Xirvik
2026-03-01 16:39:45 +00:00
committed by Jari Sundell
parent 38fc815d52
commit 598914908f
10 changed files with 199 additions and 21 deletions
+106
View File
@@ -19,6 +19,11 @@ void initialize_commands();
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::function>::type>(key, slot, &rpc::function, \
rpc::CommandMap::flag_dont_delete, NULL, NULL);
#define CMD2_A_FUNCTION_U(key, function, slot, parm, doc) \
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::function>::type>(key, slot, &rpc::function, \
rpc::CommandMap::flag_dont_delete | rpc::CommandMap::flag_public_rpc | \
rpc::CommandMap::flag_untrusted_safe, NULL, NULL);
#define CMD2_ANY(key, slot) CMD2_A_FUNCTION(key, command_base_call<rpc::target_type>, slot, "i:", "")
#define CMD2_ANY_P(key, slot) CMD2_A_FUNCTION_PRIVATE(key, command_base_call<rpc::target_type>, slot, "i:", "")
@@ -35,6 +40,42 @@ void initialize_commands();
#define CMD2_ANY_LIST(key, slot) CMD2_A_FUNCTION(key, command_base_call_list<rpc::target_type>, slot, "i:", "")
#define CMD2_ANY_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<rpc::target_type>, slot, "i:", "")
#define CMD2_ANY_VOID_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<rpc::target_type>, object_convert_void(slot), "i:", "")
#define CMD2_ANY_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_list<rpc::target_type>, object_convert_void(slot), "i:", "")
#define CMD2_ANY_L_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_list<rpc::target_type>, slot, "A:", "")
#define CMD2_ANY_VALUE_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_value<rpc::target_type>, slot, "i:i", "")
#define CMD2_ANY_VALUE_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_value<rpc::target_type>, object_convert_void(slot), "i:i", "")
#define CMD2_ANY_VALUE_KB_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_value_kb<rpc::target_type>, object_convert_void(slot), "i:i", "")
#define CMD2_ANY_STRING_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_string<rpc::target_type>, slot, "i:s", "")
#define CMD2_ANY_STRING_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_string<rpc::target_type>, object_convert_void(slot), "i:s", "")
#define CMD2_ANY_LIST_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_list<rpc::target_type>, slot, "i:", "")
#define CMD2_DL_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<core::Download*>, slot, "i:", "")
#define CMD2_DL_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<core::Download*>, object_convert_void(slot), "i:", "")
#define CMD2_DL_VALUE_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_value<core::Download*>, slot, "i:", "")
#define CMD2_DL_VALUE_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_value<core::Download*>, object_convert_void(slot), "i:", "")
#define CMD2_DL_STRING_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_string<core::Download*>, slot, "i:", "")
#define CMD2_DL_STRING_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_string<core::Download*>, object_convert_void(slot), "i:", "")
#define CMD2_DL_LIST_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_list<core::Download*>, slot, "i:", "")
#define CMD2_FILE_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<torrent::File*>, slot, "i:", "")
#define CMD2_FILE_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<torrent::File*>, object_convert_void(slot), "i:", "")
#define CMD2_FILE_VALUE_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_value<torrent::File*>, object_convert_void(slot), "i:i", "")
#define CMD2_FILEITR_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<torrent::FileListIterator*>, slot, "i:", "")
#define CMD2_PEER_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<torrent::Peer*>, slot, "i:", "")
#define CMD2_PEER_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<torrent::Peer*>, object_convert_void(slot), "i:", "")
#define CMD2_PEER_VALUE_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_value<torrent::Peer*>, object_convert_void(slot), "i:i", "")
#define CMD2_TRACKER_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<torrent::tracker::Tracker*>, slot, "i:", "")
#define CMD2_TRACKER_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call<torrent::tracker::Tracker*>, object_convert_void(slot), "i:", "")
#define CMD2_TRACKER_VALUE_V_U(key, slot) CMD2_A_FUNCTION_U(key, command_base_call_value<torrent::tracker::Tracker*>, object_convert_void(slot), "i:i", "")
#define CMD2_DL(key, slot) CMD2_A_FUNCTION(key, command_base_call<core::Download*>, slot, "i:", "")
#define CMD2_DL_V(key, slot) CMD2_A_FUNCTION(key, command_base_call<core::Download*>, object_convert_void(slot), "i:", "")
#define CMD2_DL_VALUE(key, slot) CMD2_A_FUNCTION(key, command_base_call_value<core::Download*>, slot, "i:", "")
@@ -96,6 +137,71 @@ void initialize_commands();
CMD2_ANY_VOID(key ".push_back", std::bind(&rpc::object_storage::list_push_back, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_BOOL_U(key, value) \
control->object_storage()->insert_c_str(key, int64_t(value), rpc::object_storage::flag_bool_type); \
CMD2_ANY_U(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_VALUE_U(key ".set", std::bind(&rpc::object_storage::set_bool, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_BOOL_U_GET(key, value) \
control->object_storage()->insert_c_str(key, int64_t(value), rpc::object_storage::flag_bool_type); \
CMD2_ANY_U(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_VALUE(key ".set", std::bind(&rpc::object_storage::set_bool, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_VALUE_U(key, value) \
control->object_storage()->insert_c_str(key, int64_t(value), rpc::object_storage::flag_value_type); \
CMD2_ANY_U(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_VALUE_U(key ".set", std::bind(&rpc::object_storage::set_value, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_VALUE_U_GET(key, value) \
control->object_storage()->insert_c_str(key, int64_t(value), rpc::object_storage::flag_value_type); \
CMD2_ANY_U(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_VALUE(key ".set", std::bind(&rpc::object_storage::set_value, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_STRING_U(key, value) \
control->object_storage()->insert_c_str(key, value, rpc::object_storage::flag_string_type); \
CMD2_ANY_U(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_STRING_U(key ".set", std::bind(&rpc::object_storage::set_string, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_STRING_U_GET(key, value) \
control->object_storage()->insert_c_str(key, value, rpc::object_storage::flag_string_type); \
CMD2_ANY_U(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_STRING(key ".set", std::bind(&rpc::object_storage::set_string, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_C_STRING_U(key, value) \
control->object_storage()->insert_c_str(key, value, rpc::object_storage::flag_string_type); \
CMD2_ANY_U(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key)));
#define CMD2_VAR_LIST_U(key) \
control->object_storage()->insert_c_str(key, torrent::Object::create_list(), rpc::object_storage::flag_list_type); \
CMD2_ANY_U(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_LIST_U(key ".set", std::bind(&rpc::object_storage::set_list, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2)); \
CMD2_ANY_VOID_U(key ".push_back", std::bind(&rpc::object_storage::list_push_back, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_VAR_LIST_U_GET(key) \
control->object_storage()->insert_c_str(key, torrent::Object::create_list(), rpc::object_storage::flag_list_type); \
CMD2_ANY_U(key, std::bind(&rpc::object_storage::get, control->object_storage(), \
torrent::raw_string::from_c_str(key))); \
CMD2_ANY_LIST(key ".set", std::bind(&rpc::object_storage::set_list, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2)); \
CMD2_ANY_VOID(key ".push_back", std::bind(&rpc::object_storage::list_push_back, control->object_storage(), \
torrent::raw_string::from_c_str(key), std::placeholders::_2));
#define CMD2_FUNC_SINGLE(key, cmds) \
CMD2_ANY(key, std::bind(&rpc::command_function_call_object, torrent::Object(torrent::raw_string::from_c_str(cmds)), \
std::placeholders::_1, std::placeholders::_2));