Implement permission for stream

This commit is contained in:
Yukino Song
2024-09-15 22:40:38 +08:00
parent aa4d38e992
commit 2e7bde8958
6 changed files with 150 additions and 41 deletions
+36 -8
View File
@@ -38,16 +38,44 @@ namespace crypto {
* @brief The permissions of a client. * @brief The permissions of a client.
*/ */
enum class PERM: uint32_t { enum class PERM: uint32_t {
list = 1 << 0, // Allow list apps _reserved = 1,
view = 1 << 3, // Allow view streams
input = 1 << 7, // Allow any input: kbd/mouse, controller _input = _reserved << 8, // Input permission group
server_cmd = 1 << 10, // Allow execute server cmd input_controller = _input << 0, // Allow controller input
launch = 1 << 15, // Allow launch apps input_touch = _input << 1, // Allow touch input
_default = view | list, // Default permissions for new clients input_pen = _input << 2, // Allow pen input
_all = launch | server_cmd | input | view | list, // All current permissions input_kbdm = _input << 3, // Allow kbd/mouse input
_no = 0, // No permissions are granted _all_inputs = input_controller | input_touch | input_pen | input_kbdm,
_operation = _input << 8, // Operation permission group
clipboard_set = _operation << 0, // Allow set clipboard from client
clipboard_read = _operation << 1, // Allow read clipboard from host
file_upload = _operation << 2, // Allow upload files to host
file_dwnload = _operation << 3, // Allow download files from host
server_cmd = _operation << 4, // Allow execute server cmd
_all_opeiations = clipboard_set | clipboard_read | file_upload | file_dwnload | server_cmd,
_action = _operation << 8, // Action permission group
list = _action << 0, // Allow list apps
view = _action << 1, // Allow view streams
launch = _action << 2, // Allow launch apps
_all_actions = list | view | launch,
_default = view | list, // Default permissions for new clients
_no = 0, // No permissions are granted
_all = _all_inputs | _all_opeiations | _all_actions, // All current permissions
}; };
inline constexpr PERM
operator&(PERM x, PERM y) {
return static_cast<PERM>(static_cast<uint32_t>(x) & static_cast<uint32_t>(y));
}
inline constexpr bool
operator!(PERM p) {
return static_cast<uint32_t>(p) == 0;
}
struct named_cert_t { struct named_cert_t {
std::string name; std::string name;
std::string uuid; std::string uuid;
+55 -1
View File
@@ -1628,7 +1628,61 @@ namespace input {
* @param input_data The input message. * @param input_data The input message.
*/ */
void void
passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data) { passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data, const crypto::PERM& permission) {
// No input permissions at all
if (!(permission & crypto::PERM::_all_inputs)) {
return;
}
// Have some input permission
// Otherwise have all input permission
if ((permission & crypto::PERM::_all_inputs) != crypto::PERM::_all_inputs) {
PNV_INPUT_HEADER payload = (PNV_INPUT_HEADER)input_data.data();
// Check permission
switch (util::endian::little(payload->magic)) {
case MULTI_CONTROLLER_MAGIC_GEN5:
case SS_CONTROLLER_ARRIVAL_MAGIC:
case SS_CONTROLLER_TOUCH_MAGIC:
case SS_CONTROLLER_MOTION_MAGIC:
case SS_CONTROLLER_BATTERY_MAGIC:
if (!(permission & crypto::PERM::input_controller)) {
return;
} else {
break;
}
case MOUSE_MOVE_REL_MAGIC_GEN5:
case MOUSE_MOVE_ABS_MAGIC:
case MOUSE_BUTTON_DOWN_EVENT_MAGIC_GEN5:
case MOUSE_BUTTON_UP_EVENT_MAGIC_GEN5:
case SCROLL_MAGIC_GEN5:
case SS_HSCROLL_MAGIC:
case KEY_DOWN_EVENT_MAGIC:
case KEY_UP_EVENT_MAGIC:
case UTF8_TEXT_EVENT_MAGIC:
if (!(permission & crypto::PERM::input_kbdm)) {
return;
} else {
break;
}
case SS_TOUCH_MAGIC:
if (!(permission & crypto::PERM::input_touch)) {
return;
} else {
break;
}
case SS_PEN_MAGIC:
if (!(permission & crypto::PERM::input_pen)) {
return;
} else {
break;
}
default:
// Unknown input event
return;
}
}
{ {
std::lock_guard<std::mutex> lg(input->input_queue_lock); std::lock_guard<std::mutex> lg(input->input_queue_lock);
input->input_queue.push_back(std::move(input_data)); input->input_queue.push_back(std::move(input_data));
+2 -1
View File
@@ -8,6 +8,7 @@
#include "platform/common.h" #include "platform/common.h"
#include "thread_safe.h" #include "thread_safe.h"
#include "crypto.h"
namespace input { namespace input {
struct input_t; struct input_t;
@@ -17,7 +18,7 @@ namespace input {
void void
reset(std::shared_ptr<input_t> &input); reset(std::shared_ptr<input_t> &input);
void void
passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data); passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data, const crypto::PERM& permission);
[[nodiscard]] std::unique_ptr<platf::deinit_t> [[nodiscard]] std::unique_ptr<platf::deinit_t>
init(); init();
+29 -28
View File
@@ -314,7 +314,7 @@ namespace nvhttp {
named_cert_p->name = el.get<std::string>("name"); named_cert_p->name = el.get<std::string>("name");
named_cert_p->cert = el.get<std::string>("cert"); named_cert_p->cert = el.get<std::string>("cert");
named_cert_p->uuid = el.get<std::string>("uuid"); named_cert_p->uuid = el.get<std::string>("uuid");
named_cert_p->perm = (PERM)el.get("perm", (uint32_t)PERM::_all); named_cert_p->perm = (PERM)el.get("perm", (uint32_t)PERM::_all) & PERM::_all;
client.named_devices.emplace_back(named_cert_p); client.named_devices.emplace_back(named_cert_p);
} }
} }
@@ -340,7 +340,7 @@ namespace nvhttp {
} }
std::shared_ptr<rtsp_stream::launch_session_t> std::shared_ptr<rtsp_stream::launch_session_t>
make_launch_session(bool host_audio, const args_t &args, const std::string& device_name, const std::string& uuid) { make_launch_session(bool host_audio, const args_t &args, const crypto::named_cert_t* named_cert_p) {
auto launch_session = std::make_shared<rtsp_stream::launch_session_t>(); auto launch_session = std::make_shared<rtsp_stream::launch_session_t>();
launch_session->id = ++session_id_counter; launch_session->id = ++session_id_counter;
@@ -360,8 +360,9 @@ namespace nvhttp {
x++; x++;
} }
launch_session->device_name = device_name.empty() ? "ApolloDisplay"s : device_name; launch_session->device_name = named_cert_p->name.empty() ? "ApolloDisplay"s : named_cert_p->name;
launch_session->unique_id = uuid; launch_session->unique_id = named_cert_p->uuid;
launch_session->perm = named_cert_p->perm;
launch_session->appid = util::from_view(get_arg(args, "appid", "unknown")); launch_session->appid = util::from_view(get_arg(args, "appid", "unknown"));
launch_session->enable_sops = util::from_view(get_arg(args, "sops", "0")); launch_session->enable_sops = util::from_view(get_arg(args, "sops", "0"));
launch_session->surround_info = util::from_view(get_arg(args, "surroundAudioInfo", "196610")); launch_session->surround_info = util::from_view(get_arg(args, "surroundAudioInfo", "196610"));
@@ -797,15 +798,13 @@ namespace nvhttp {
tree.put("root.ExternalPort", net::map_port(PORT_HTTP)); tree.put("root.ExternalPort", net::map_port(PORT_HTTP));
tree.put("root.MaxLumaPixelsHEVC", video::active_hevc_mode > 1 ? "1869449984" : "0"); tree.put("root.MaxLumaPixelsHEVC", video::active_hevc_mode > 1 ? "1869449984" : "0");
crypto::named_cert_t* named_cert_p;
// Only include the MAC address for requests sent from paired clients over HTTPS. // Only include the MAC address for requests sent from paired clients over HTTPS.
// For HTTP requests, use a placeholder MAC address that Moonlight knows to ignore. // For HTTP requests, use a placeholder MAC address that Moonlight knows to ignore.
if constexpr (std::is_same_v<SunshineHTTPS, T>) { if constexpr (std::is_same_v<SunshineHTTPS, T>) {
tree.put("root.mac", platf::get_mac_address(net::addr_to_normalized_string(local_endpoint.address()))); tree.put("root.mac", platf::get_mac_address(net::addr_to_normalized_string(local_endpoint.address())));
named_cert_p = get_verified_cert(request); auto named_cert_p = get_verified_cert(request);
if (named_cert_p->perm >= PERM::server_cmd) { if (!!(named_cert_p->perm & PERM::server_cmd)) {
pt::ptree& root_node = tree.get_child("root"); pt::ptree& root_node = tree.get_child("root");
if (config::sunshine.server_cmds.size() > 0) { if (config::sunshine.server_cmds.size() > 0) {
@@ -820,12 +819,14 @@ namespace nvhttp {
BOOST_LOG(debug) << "Permission Get ServerCommand denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")"; BOOST_LOG(debug) << "Permission Get ServerCommand denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")";
} }
tree.put("root.Permission", std::to_string((uint32_t)named_cert_p->perm));
#ifdef _WIN32 #ifdef _WIN32
tree.put("root.VirtualDisplayCapable", true); tree.put("root.VirtualDisplayCapable", true);
if (named_cert_p->perm < PERM::list) { if (!!(named_cert_p->perm & PERM::list)) {
tree.put("root.VirtualDisplayDriverReady", true);
} else {
tree.put("root.VirtualDisplayDriverReady", proc::vDisplayDriverStatus == VDISPLAY::DRIVER_STATUS::OK); tree.put("root.VirtualDisplayDriverReady", proc::vDisplayDriverStatus == VDISPLAY::DRIVER_STATUS::OK);
} else {
tree.put("root.VirtualDisplayDriverReady", true);
} }
#endif #endif
} }
@@ -931,7 +932,17 @@ namespace nvhttp {
apps.put("<xmlattr>.status_code", 200); apps.put("<xmlattr>.status_code", 200);
auto named_cert_p = get_verified_cert(request); auto named_cert_p = get_verified_cert(request);
if (named_cert_p->perm < PERM::list) { if (!!(named_cert_p->perm & PERM::list)) {
for (auto &proc : proc::proc.get_apps()) {
pt::ptree app;
app.put("IsHdrSupported"s, video::active_hevc_mode == 3 ? 1 : 0);
app.put("AppTitle"s, proc.name);
app.put("ID", proc.id);
apps.push_back(std::make_pair("App", std::move(app)));
}
} else {
BOOST_LOG(debug) << "Permission ListApp denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")"; BOOST_LOG(debug) << "Permission ListApp denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")";
pt::ptree app; pt::ptree app;
@@ -943,16 +954,6 @@ namespace nvhttp {
apps.push_back(std::make_pair("App", std::move(app))); apps.push_back(std::make_pair("App", std::move(app)));
return; return;
} else {
for (auto &proc : proc::proc.get_apps()) {
pt::ptree app;
app.put("IsHdrSupported"s, video::active_hevc_mode == 3 ? 1 : 0);
app.put("AppTitle"s, proc.name);
app.put("ID", proc.id);
apps.push_back(std::make_pair("App", std::move(app)));
}
} }
} }
@@ -971,7 +972,7 @@ namespace nvhttp {
}); });
auto named_cert_p = get_verified_cert(request); auto named_cert_p = get_verified_cert(request);
if (named_cert_p->perm < PERM::launch) { if (!(named_cert_p->perm & PERM::launch)) {
BOOST_LOG(debug) << "Permission LaunchApp denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")"; BOOST_LOG(debug) << "Permission LaunchApp denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")";
tree.put("root.resume", 0); tree.put("root.resume", 0);
@@ -1012,7 +1013,7 @@ namespace nvhttp {
} }
host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); host_audio = util::from_view(get_arg(args, "localAudioPlayMode"));
auto launch_session = make_launch_session(host_audio, args, named_cert_p->name, named_cert_p->uuid); auto launch_session = make_launch_session(host_audio, args, named_cert_p);
auto encryption_mode = net::encryption_mode_for_address(request->remote_endpoint().address()); auto encryption_mode = net::encryption_mode_for_address(request->remote_endpoint().address());
if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) { if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) {
@@ -1079,7 +1080,7 @@ namespace nvhttp {
}); });
auto named_cert_p = get_verified_cert(request); auto named_cert_p = get_verified_cert(request);
if (named_cert_p->perm < PERM::view) { if (!(named_cert_p->perm & PERM::view)) {
BOOST_LOG(debug) << "Permission ViewApp denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")"; BOOST_LOG(debug) << "Permission ViewApp denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")";
tree.put("root.resume", 0); tree.put("root.resume", 0);
@@ -1140,7 +1141,7 @@ namespace nvhttp {
} }
} }
auto launch_session = make_launch_session(host_audio, args, named_cert_p->name, named_cert_p->uuid); auto launch_session = make_launch_session(host_audio, args, named_cert_p);
auto encryption_mode = net::encryption_mode_for_address(request->remote_endpoint().address()); auto encryption_mode = net::encryption_mode_for_address(request->remote_endpoint().address());
if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) { if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) {
@@ -1176,7 +1177,7 @@ namespace nvhttp {
}); });
auto named_cert_p = get_verified_cert(request); auto named_cert_p = get_verified_cert(request);
if (named_cert_p->perm < PERM::launch) { if (!(named_cert_p->perm & PERM::launch)) {
BOOST_LOG(debug) << "Permission CancelApp denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")"; BOOST_LOG(debug) << "Permission CancelApp denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")";
tree.put("root.resume", 0); tree.put("root.resume", 0);
@@ -1215,7 +1216,7 @@ namespace nvhttp {
auto named_cert_p = get_verified_cert(request); auto named_cert_p = get_verified_cert(request);
if (named_cert_p->perm < PERM::list) { if (!(named_cert_p->perm & PERM::list)) {
BOOST_LOG(debug) << "Permission Get AppAsset denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")"; BOOST_LOG(debug) << "Permission Get AppAsset denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")";
fg.disable(); fg.disable();
+3 -1
View File
@@ -21,9 +21,11 @@ namespace rtsp_stream {
std::string av_ping_payload; std::string av_ping_payload;
uint32_t control_connect_data; uint32_t control_connect_data;
bool host_audio;
std::string device_name; std::string device_name;
std::string unique_id; std::string unique_id;
crypto::PERM perm;
bool host_audio;
int width; int width;
int height; int height;
int fps; int fps;
+25 -2
View File
@@ -20,6 +20,7 @@ extern "C" {
} }
#include "config.h" #include "config.h"
#include "crypto.h"
#include "globals.h" #include "globals.h"
#include "input.h" #include "input.h"
#include "logging.h" #include "logging.h"
@@ -403,6 +404,9 @@ namespace stream {
} control; } control;
std::uint32_t launch_session_id; std::uint32_t launch_session_id;
std::string device_name;
std::string device_uuid;
crypto::PERM permission;
safe::mail_raw_t::event_t<bool> shutdown_event; safe::mail_raw_t::event_t<bool> shutdown_event;
safe::signal_t controlEnd; safe::signal_t controlEnd;
@@ -992,11 +996,17 @@ namespace stream {
std::copy(payload.end() - 16, payload.end(), std::begin(iv)); std::copy(payload.end() - 16, payload.end(), std::begin(iv));
} }
input::passthrough(session->input, std::move(plaintext)); input::passthrough(session->input, std::move(plaintext), session->permission);
}); });
server->map(packetTypes[IDX_EXEC_SERVER_CMD], [server](session_t *session, const std::string_view &payload) { server->map(packetTypes[IDX_EXEC_SERVER_CMD], [server](session_t *session, const std::string_view &payload) {
BOOST_LOG(debug) << "type [IDX_EXEC_SERVER_CMD]"sv; BOOST_LOG(debug) << "type [IDX_EXEC_SERVER_CMD]"sv;
if (!(session->permission & crypto::PERM::server_cmd)) {
BOOST_LOG(debug) << "Permission Exec Server Cmd deined for [" << session->device_name << "]";
return;
}
uint8_t cmdIndex = *(uint8_t*)payload.data(); uint8_t cmdIndex = *(uint8_t*)payload.data();
if (cmdIndex < config::sunshine.server_cmds.size()) { if (cmdIndex < config::sunshine.server_cmds.size()) {
@@ -1024,10 +1034,20 @@ namespace stream {
server->map(packetTypes[IDX_SET_CLIPBOARD], [server](session_t *session, const std::string_view &payload) { server->map(packetTypes[IDX_SET_CLIPBOARD], [server](session_t *session, const std::string_view &payload) {
BOOST_LOG(info) << "type [IDX_SET_CLIPBOARD]: "sv << payload << " size: " << payload.size(); BOOST_LOG(info) << "type [IDX_SET_CLIPBOARD]: "sv << payload << " size: " << payload.size();
if (!(session->permission & crypto::PERM::clipboard_set)) {
BOOST_LOG(debug) << "Permission Clipboard Set deined for [" << session->device_name << "]";
return;
}
}); });
server->map(packetTypes[IDX_FILE_TRANSFER_NONCE_REQUEST], [server](session_t *session, const std::string_view &payload) { server->map(packetTypes[IDX_FILE_TRANSFER_NONCE_REQUEST], [server](session_t *session, const std::string_view &payload) {
BOOST_LOG(info) << "type [IDX_FILE_TRANSFER_NONCE_REQUEST]: "sv << payload << " size: " << payload.size(); BOOST_LOG(info) << "type [IDX_FILE_TRANSFER_NONCE_REQUEST]: "sv << payload << " size: " << payload.size();
if (!(session->permission & crypto::PERM::file_upload)) {
BOOST_LOG(debug) << "Permission File Upload deined for [" << session->device_name << "]";
return;
}
}); });
server->map(packetTypes[IDX_ENCRYPTED], [server](session_t *session, const std::string_view &payload) { server->map(packetTypes[IDX_ENCRYPTED], [server](session_t *session, const std::string_view &payload) {
@@ -1091,7 +1111,7 @@ namespace stream {
// IDX_INPUT_DATA callback will attempt to decrypt unencrypted data, therefore we need pass it directly // IDX_INPUT_DATA callback will attempt to decrypt unencrypted data, therefore we need pass it directly
if (type == packetTypes[IDX_INPUT_DATA]) { if (type == packetTypes[IDX_INPUT_DATA]) {
plaintext.erase(std::begin(plaintext), std::begin(plaintext) + 4); plaintext.erase(std::begin(plaintext), std::begin(plaintext) + 4);
input::passthrough(session->input, std::move(plaintext)); input::passthrough(session->input, std::move(plaintext), session->permission);
} }
else { else {
server->call(type, session, next_payload, true); server->call(type, session, next_payload, true);
@@ -2051,6 +2071,9 @@ namespace stream {
session->shutdown_event = mail->event<bool>(mail::shutdown); session->shutdown_event = mail->event<bool>(mail::shutdown);
session->launch_session_id = launch_session.id; session->launch_session_id = launch_session.id;
session->device_name = launch_session.device_name;
session->device_uuid = launch_session.unique_id;
session->permission = launch_session.perm;
session->config = config; session->config = config;