mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-15 00:09:39 +08:00
[fix] route unelevated Windows input through service
This commit is contained in:
@@ -19,8 +19,10 @@
|
|||||||
#include "runtime/gui_runtime.h"
|
#include "runtime/gui_runtime.h"
|
||||||
#include "speaker_capturer_factory.h"
|
#include "speaker_capturer_factory.h"
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
#include "platform/windows/screen_capturer/screen_capturer_win.h"
|
#include "platform/windows/screen_capturer/screen_capturer_win.h"
|
||||||
#include "platform/windows/input/mouse/mouse_controller.h"
|
#include "platform/windows/input/mouse/mouse_controller.h"
|
||||||
|
#include "service_host.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
@@ -327,6 +329,37 @@ int SessionDeviceManager::StopScreenCapturer() {
|
|||||||
void SessionDeviceManager::ReleaseRemoteMouseButtons() {
|
void SessionDeviceManager::ReleaseRemoteMouseButtons() {
|
||||||
if (auto mouse = dynamic_cast<PlatformMouseController*>(mouse_controller_))
|
if (auto mouse = dynamic_cast<PlatformMouseController*>(mouse_controller_))
|
||||||
mouse->ReleasePressedButtons();
|
mouse->ReleasePressedButtons();
|
||||||
|
std::lock_guard lock(service_mouse_mutex_);
|
||||||
|
for (int button = 0; button < 3; ++button) {
|
||||||
|
const unsigned bit = 1u << button;
|
||||||
|
if (!(service_mouse_buttons_ & bit)) continue;
|
||||||
|
const auto response = SendCrossDeskSecureDesktopMouseInput(
|
||||||
|
service_mouse_x_, service_mouse_y_, 0, (button + 1) * 2, 1000);
|
||||||
|
const auto json = nlohmann::json::parse(response, nullptr, false);
|
||||||
|
if (json.is_object() && json.value("ok", false)) {
|
||||||
|
service_mouse_buttons_ &= ~bit;
|
||||||
|
} else {
|
||||||
|
LOG_WARN("Service mouse button release failed: button={}, response={}",
|
||||||
|
button, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SessionDeviceManager::SendServiceMouseCommand(int x, int y,
|
||||||
|
int wheel, int flag) {
|
||||||
|
std::lock_guard lock(service_mouse_mutex_);
|
||||||
|
service_mouse_x_ = x;
|
||||||
|
service_mouse_y_ = y;
|
||||||
|
const unsigned bit = flag >= 1 && flag <= 6 ? 1u << ((flag - 1) / 2) : 0;
|
||||||
|
// A timeout can follow successful injection. Keep downs for disconnect
|
||||||
|
// cleanup even if the reply was lost; only clear a confirmed release.
|
||||||
|
if (flag % 2 == 1) service_mouse_buttons_ |= bit;
|
||||||
|
const auto response = SendCrossDeskSecureDesktopMouseInput(x, y, wheel, flag,
|
||||||
|
1000);
|
||||||
|
const auto json = nlohmann::json::parse(response, nullptr, false);
|
||||||
|
if (flag % 2 == 0 && json.is_object() && json.value("ok", false))
|
||||||
|
service_mouse_buttons_ &= ~bit;
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -412,6 +445,9 @@ int SessionDeviceManager::StartMouseController() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int SessionDeviceManager::StopMouseController() {
|
int SessionDeviceManager::StopMouseController() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
ReleaseRemoteMouseButtons();
|
||||||
|
#endif
|
||||||
if (mouse_controller_) {
|
if (mouse_controller_) {
|
||||||
mouse_controller_->Destroy();
|
mouse_controller_->Destroy();
|
||||||
delete mouse_controller_;
|
delete mouse_controller_;
|
||||||
@@ -718,11 +754,7 @@ std::vector<HostDisplay> SessionDeviceManager::host_display_list() const {
|
|||||||
|
|
||||||
void SessionDeviceManager::DestroyDevices() {
|
void SessionDeviceManager::DestroyDevices() {
|
||||||
speaker_capture_.Shutdown();
|
speaker_capture_.Shutdown();
|
||||||
if (mouse_controller_) {
|
StopMouseController();
|
||||||
mouse_controller_->Destroy();
|
|
||||||
delete mouse_controller_;
|
|
||||||
mouse_controller_ = nullptr;
|
|
||||||
}
|
|
||||||
if (screen_capturer_) {
|
if (screen_capturer_) {
|
||||||
screen_capturer_->Destroy();
|
screen_capturer_->Destroy();
|
||||||
delete screen_capturer_;
|
delete screen_capturer_;
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ public:
|
|||||||
int StopScreenCapturer();
|
int StopScreenCapturer();
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void ReleaseRemoteMouseButtons();
|
void ReleaseRemoteMouseButtons();
|
||||||
|
std::string SendServiceMouseCommand(int x, int y, int wheel, int flag);
|
||||||
#endif
|
#endif
|
||||||
// Requests are asynchronous; Stop drains/revokes audio delivery before
|
// Requests are asynchronous; Stop drains/revokes audio delivery before
|
||||||
// returning so the connection peer can be destroyed safely.
|
// returning so the connection peer can be destroyed safely.
|
||||||
@@ -107,6 +108,9 @@ private:
|
|||||||
CapturedKeyboardQueue captured_keyboard_inputs_;
|
CapturedKeyboardQueue captured_keyboard_inputs_;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::chrono::steady_clock::time_point next_keyboard_capture_retry_{};
|
std::chrono::steady_clock::time_point next_keyboard_capture_retry_{};
|
||||||
|
std::mutex service_mouse_mutex_;
|
||||||
|
unsigned service_mouse_buttons_ = 0;
|
||||||
|
int service_mouse_x_ = 0, service_mouse_y_ = 0;
|
||||||
#endif
|
#endif
|
||||||
std::chrono::steady_clock::time_point last_frame_time_{};
|
std::chrono::steady_clock::time_point last_frame_time_{};
|
||||||
std::chrono::steady_clock::time_point next_frame_deadline_{};
|
std::chrono::steady_clock::time_point next_frame_deadline_{};
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "desktop_transition_policy.h"
|
#include "desktop_transition_policy.h"
|
||||||
#include "interactive_desktop.h"
|
#include "interactive_desktop.h"
|
||||||
#include "service_host.h"
|
#include "service_host.h"
|
||||||
|
#include "unattended_config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
@@ -229,10 +230,13 @@ bool KeyboardController::InjectRemoteKey(int key_code, bool is_down,
|
|||||||
owner_.privacy_.SuspendForDesktop();
|
owner_.privacy_.SuspendForDesktop();
|
||||||
}
|
}
|
||||||
if (owner_.is_server_mode_ || !owner_.WindowsInputStage().empty()) {
|
if (owner_.is_server_mode_ || !owner_.WindowsInputStage().empty()) {
|
||||||
|
static const bool process_elevated = IsAdministratorProcess();
|
||||||
return DispatchDesktopInput(
|
return DispatchDesktopInput(
|
||||||
[&] {
|
[&] {
|
||||||
return PreferUserDesktopInput(IsCurrentSessionUserDesktopActive(),
|
return PreferUserDesktopInput(IsCurrentSessionUserDesktopActive(),
|
||||||
owner_.windows_consent_ui_.load());
|
owner_.windows_consent_ui_.load(),
|
||||||
|
owner_.local_service_available_.load(),
|
||||||
|
process_elevated);
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
SetLastError(ERROR_SUCCESS);
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "desktop_transition_policy.h"
|
#include "desktop_transition_policy.h"
|
||||||
#include "interactive_desktop.h"
|
#include "interactive_desktop.h"
|
||||||
#include "service_host.h"
|
#include "service_host.h"
|
||||||
|
#include "unattended_config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
@@ -394,11 +395,14 @@ void PeerEventHandler::OnReceiveDataBuffer(
|
|||||||
}
|
}
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
if (runtime->is_server_mode_ && remote_action.type == ControlType::mouse) {
|
if (runtime->is_server_mode_ && remote_action.type == ControlType::mouse) {
|
||||||
|
static const bool process_elevated = IsAdministratorProcess();
|
||||||
std::string response;
|
std::string response;
|
||||||
const bool delivered = DispatchDesktopInput(
|
const bool delivered = DispatchDesktopInput(
|
||||||
[&] {
|
[&] {
|
||||||
return PreferUserDesktopInput(IsCurrentSessionUserDesktopActive(),
|
return PreferUserDesktopInput(
|
||||||
runtime->windows_consent_ui_.load());
|
IsCurrentSessionUserDesktopActive(),
|
||||||
|
runtime->windows_consent_ui_.load(),
|
||||||
|
runtime->local_service_available_.load(), process_elevated);
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
SetLastError(ERROR_SUCCESS);
|
SetLastError(ERROR_SUCCESS);
|
||||||
@@ -425,9 +429,9 @@ void PeerEventHandler::OnReceiveDataBuffer(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
response = SendCrossDeskSecureDesktopMouseInput(
|
response = runtime->devices_.SendServiceMouseCommand(
|
||||||
absolute_x, absolute_y, remote_action.m.s,
|
absolute_x, absolute_y, remote_action.m.s,
|
||||||
static_cast<int>(remote_action.m.flag), 1000);
|
static_cast<int>(remote_action.m.flag));
|
||||||
auto json = nlohmann::json::parse(response, nullptr, false);
|
auto json = nlohmann::json::parse(response, nullptr, false);
|
||||||
const bool sent = json.is_object() && json.value("ok", false);
|
const bool sent = json.is_object() && json.value("ok", false);
|
||||||
const std::string error =
|
const std::string error =
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ struct PlatformIntegrationState {
|
|||||||
std::atomic<bool> windows_consent_ui_{false};
|
std::atomic<bool> windows_consent_ui_{false};
|
||||||
std::atomic<bool> pending_windows_service_sas_{false};
|
std::atomic<bool> pending_windows_service_sas_{false};
|
||||||
bool local_service_status_received_ = false;
|
bool local_service_status_received_ = false;
|
||||||
bool local_service_available_ = false;
|
std::atomic<bool> local_service_available_{false};
|
||||||
std::string local_interactive_stage_;
|
std::string local_interactive_stage_;
|
||||||
uint32_t last_local_secure_input_block_log_tick_ = 0;
|
uint32_t last_local_secure_input_block_log_tick_ = 0;
|
||||||
uint32_t last_windows_service_status_tick_ = 0;
|
uint32_t last_windows_service_status_tick_ = 0;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "desktop_transition_policy.h"
|
#include "desktop_transition_policy.h"
|
||||||
#include "interactive_state.h"
|
#include "interactive_state.h"
|
||||||
#include "service_host.h"
|
#include "service_host.h"
|
||||||
|
#include "unattended_config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
@@ -205,7 +206,12 @@ void GuiRuntime::HandleWindowsServiceIntegration() {
|
|||||||
status.interactive_stage == "user-desktop";
|
status.interactive_stage == "user-desktop";
|
||||||
local_service_status_received_ =
|
local_service_status_received_ =
|
||||||
status_ok || previous_secure_desktop_interaction;
|
status_ok || previous_secure_desktop_interaction;
|
||||||
local_service_available_ = status.available;
|
const bool service_was_available =
|
||||||
|
local_service_available_.exchange(status.available);
|
||||||
|
if (status.available && !service_was_available && !IsAdministratorProcess()) {
|
||||||
|
LOG_INFO("Unelevated host input routed through Windows service, including "
|
||||||
|
"elevated applications on the user desktop");
|
||||||
|
}
|
||||||
if (status.available) {
|
if (status.available) {
|
||||||
if (keep_optimistic_secure_desktop) {
|
if (keep_optimistic_secure_desktop) {
|
||||||
local_interactive_stage_ = "secure-desktop";
|
local_interactive_stage_ = "secure-desktop";
|
||||||
|
|||||||
@@ -19,6 +19,18 @@ inline bool PreferUserDesktopInput(bool user_desktop_active,
|
|||||||
return user_desktop_active && !consent_ui_visible;
|
return user_desktop_active && !consent_ui_visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool PreferUserDesktopInput(bool user_desktop_active,
|
||||||
|
bool consent_ui_visible,
|
||||||
|
bool service_available,
|
||||||
|
bool process_elevated) {
|
||||||
|
// An elevated application still lives on Default. SendInput only reports
|
||||||
|
// insertion into the input stream, not delivery to that application's UI.
|
||||||
|
// Use the authorized service proactively for an unelevated host, including
|
||||||
|
// clicks on an elevated window that has not become the foreground window.
|
||||||
|
return PreferUserDesktopInput(user_desktop_active, consent_ui_visible) &&
|
||||||
|
(process_elevated || !service_available);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool IsDesktopInputSetupPending(const std::string& error) {
|
inline bool IsDesktopInputSetupPending(const std::string& error) {
|
||||||
// These failures explicitly precede injection; timeouts remain ambiguous.
|
// These failures explicitly precede injection; timeouts remain ambiguous.
|
||||||
return error == "secure_input_helper_not_ready" ||
|
return error == "secure_input_helper_not_ready" ||
|
||||||
|
|||||||
@@ -1250,6 +1250,7 @@ LONG NormalizeAbsoluteMouseCoordinate(int value, int origin, int size) {
|
|||||||
INPUT BuildAbsoluteMouseMoveInput(int x, int y) {
|
INPUT BuildAbsoluteMouseMoveInput(int x, int y) {
|
||||||
INPUT input = {0};
|
INPUT input = {0};
|
||||||
input.type = INPUT_MOUSE;
|
input.type = INPUT_MOUSE;
|
||||||
|
input.mi.dwExtraInfo = crossdesk::kInjectedMouseInputMarker;
|
||||||
|
|
||||||
const int virtual_left = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
const int virtual_left = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||||
const int virtual_top = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
const int virtual_top = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
||||||
@@ -1283,6 +1284,7 @@ InputInjectionResult InjectMouseInput(const SecureMouseRequest& request) {
|
|||||||
|
|
||||||
INPUT action_input = {0};
|
INPUT action_input = {0};
|
||||||
action_input.type = INPUT_MOUSE;
|
action_input.type = INPUT_MOUSE;
|
||||||
|
action_input.mi.dwExtraInfo = crossdesk::kInjectedMouseInputMarker;
|
||||||
switch (request.flag) {
|
switch (request.flag) {
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user