mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-15 00:09:39 +08:00
[fix] restore remote input for elevated Windows apps
This commit is contained in:
@@ -238,8 +238,11 @@ bool KeyboardController::InjectRemoteKey(int key_code, bool is_down,
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
const bool sent = owner_.devices_.SendKeyboardCommand(
|
||||
key_code, is_down, scan_code, extended);
|
||||
const DWORD error = GetLastError();
|
||||
const bool blocked =
|
||||
error == ERROR_ACCESS_DENIED || error == ERROR_GEN_FAILURE;
|
||||
return DesktopInputResult{
|
||||
sent, IsDesktopTransitionInputError(GetLastError())};
|
||||
sent, IsDesktopTransitionInputError(error) || blocked, blocked};
|
||||
},
|
||||
[&]() -> DesktopInputResult {
|
||||
const std::string response = SendCrossDeskSecureDesktopKeyInput(
|
||||
|
||||
@@ -404,8 +404,11 @@ void PeerEventHandler::OnReceiveDataBuffer(
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
const bool sent = runtime->devices_.SendMouseCommand(
|
||||
remote_action, runtime->selected_display_);
|
||||
const DWORD error = GetLastError();
|
||||
const bool blocked =
|
||||
error == ERROR_ACCESS_DENIED || error == ERROR_GEN_FAILURE;
|
||||
return DesktopInputResult{
|
||||
sent, IsDesktopTransitionInputError(GetLastError())};
|
||||
sent, IsDesktopTransitionInputError(error) || blocked, blocked};
|
||||
},
|
||||
[&]() -> DesktopInputResult {
|
||||
int absolute_x = 0;
|
||||
|
||||
@@ -39,6 +39,9 @@ struct DesktopInputResult {
|
||||
// A pipe timeout may have happened after a click/key was applied: never
|
||||
// replay it.
|
||||
bool not_injected = false;
|
||||
// The ordinary desktop injector rejected its single input event (for
|
||||
// example UIPI). The authorized service can retry even on the same desktop.
|
||||
bool retry_with_service = false;
|
||||
};
|
||||
|
||||
template <typename ProbeUserDesktop, typename SendUser, typename SendSecure>
|
||||
@@ -48,6 +51,10 @@ bool DispatchDesktopInput(ProbeUserDesktop probe_user, SendUser send_user,
|
||||
const auto result = user ? send_user() : send_secure();
|
||||
if (result.delivered) return true;
|
||||
if (!result.not_injected) return false;
|
||||
if (user && result.retry_with_service) {
|
||||
// Do not replay a failed/ambiguous service request through another path.
|
||||
return send_secure().delivered;
|
||||
}
|
||||
const bool current_user = probe_user();
|
||||
if (current_user == user) return false;
|
||||
// Handle a switch between the probe and injection exactly once. Ordering is
|
||||
|
||||
@@ -38,6 +38,7 @@ constexpr wchar_t kCrossDeskClientProcessName[] = L"crossdesk.exe";
|
||||
constexpr DWORD kCrossDeskClientMonitorIntervalMs = 1000;
|
||||
constexpr ULONGLONG kCrossDeskClientMonitorStartupGraceMs = 5000;
|
||||
constexpr ULONGLONG kSasSecureDesktopGraceMs = 15000;
|
||||
constexpr ULONGLONG kUserDesktopInputIdleMs = 5000;
|
||||
|
||||
using SendSasFunction = VOID(WINAPI*)(BOOL);
|
||||
|
||||
@@ -1057,6 +1058,7 @@ int CrossDeskServiceHost::InitializeRuntime() {
|
||||
session_helper_report_state_age_ms_ = 0;
|
||||
session_helper_report_uptime_ms_ = 0;
|
||||
secure_input_helper_started_at_tick_ = 0;
|
||||
user_desktop_input_until_tick_ = 0;
|
||||
sas_secure_desktop_until_tick_ = 0;
|
||||
session_helper_process_handle_ = nullptr;
|
||||
session_helper_stop_event_ = nullptr;
|
||||
@@ -1460,6 +1462,7 @@ void CrossDeskServiceHost::RefreshSessionState() {
|
||||
ResetSessionHelperReportedStateLocked("session_changed", 0);
|
||||
sas_secure_desktop_until_tick_ = 0;
|
||||
sas_secure_desktop_seen_ = false;
|
||||
user_desktop_input_until_tick_ = 0;
|
||||
}
|
||||
process_session_id_ = process_session;
|
||||
logon_ui_visible_ = logon_ui;
|
||||
@@ -1574,7 +1577,8 @@ bool CrossDeskServiceHost::ShouldKeepSecureInputHelperLocked(
|
||||
return false;
|
||||
}
|
||||
|
||||
return HasSecureInputUiLocked() || GetEffectiveSessionLockedLocked();
|
||||
return HasSecureInputUiLocked() || GetEffectiveSessionLockedLocked() ||
|
||||
GetTickCount64() < user_desktop_input_until_tick_;
|
||||
}
|
||||
|
||||
std::string CrossDeskServiceHost::ResolveInteractiveStageLocked() const {
|
||||
@@ -1622,6 +1626,10 @@ std::string CrossDeskServiceHost::ResolveInteractiveDesktopLocked(
|
||||
return "Default";
|
||||
}
|
||||
|
||||
if (interactive_stage == "user-desktop") {
|
||||
return "Default";
|
||||
}
|
||||
|
||||
if (input_desktop_available_ && !input_desktop_name_.empty() &&
|
||||
(interactive_stage == "credential-ui" || consent_ui_visible_)) {
|
||||
return input_desktop_name_;
|
||||
@@ -2174,8 +2182,12 @@ void CrossDeskServiceHost::RecordSessionEvent(DWORD event_type,
|
||||
ResetSessionHelperReportedStateLocked("session_changed", 0);
|
||||
sas_secure_desktop_until_tick_ = 0;
|
||||
sas_secure_desktop_seen_ = false;
|
||||
user_desktop_input_until_tick_ = 0;
|
||||
}
|
||||
if (session_id == active_session_id_) {
|
||||
if (event_type == WTS_SESSION_LOGOFF) {
|
||||
user_desktop_input_until_tick_ = 0;
|
||||
}
|
||||
// Do not let an older helper sample override a session notification.
|
||||
ResetSessionHelperReportedStateLocked("session_event", 0);
|
||||
last_session_transition_tick_ = GetTickCount64();
|
||||
@@ -2501,20 +2513,28 @@ std::string CrossDeskServiceHost::ResolveSecureInputTarget(
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||
target.session_id = active_session_id_;
|
||||
if (target.session_id == 0xFFFFFFFF) {
|
||||
return BuildErrorJson("no_active_console_session");
|
||||
}
|
||||
if (target.session_id != client_session_id ||
|
||||
client_session_id != WTSGetActiveConsoleSessionId()) {
|
||||
return BuildErrorJson("service_session_mismatch");
|
||||
}
|
||||
target.stage = ResolveInteractiveStageLocked();
|
||||
target.desktop = ResolveInteractiveDesktopLocked(target.stage);
|
||||
helper_running = secure_input_helper_running_ &&
|
||||
secure_input_helper_session_id_ == target.session_id;
|
||||
can_inject = GetEffectiveSessionLockedLocked() || HasSecureInputUiLocked();
|
||||
if (target.stage == "user-desktop") {
|
||||
// Ordinary GUI input can be blocked by an elevated foreground window.
|
||||
// Only a Windows-validated client in the active console session may
|
||||
// start/retain this helper; do not misreport the desktop as UAC/locked.
|
||||
user_desktop_input_until_tick_ =
|
||||
GetTickCount64() + kUserDesktopInputIdleMs;
|
||||
can_inject = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (target.session_id == 0xFFFFFFFF) {
|
||||
return BuildErrorJson("no_active_console_session");
|
||||
}
|
||||
if (target.session_id != client_session_id ||
|
||||
client_session_id != WTSGetActiveConsoleSessionId()) {
|
||||
return BuildErrorJson("service_session_mismatch");
|
||||
}
|
||||
if (!can_inject) {
|
||||
WakeSessionState();
|
||||
return BuildErrorJson("secure_input_not_active");
|
||||
@@ -2799,8 +2819,14 @@ std::string QueryDesktopInput(const std::string& command, DWORD timeout_ms) {
|
||||
const std::string error = json.value("error", "");
|
||||
// Retry only explicit pre-injection rejections during helper startup.
|
||||
// Never replay an uncertain timeout or a partially injected mouse action.
|
||||
if (!IsDesktopInputSetupPending(error) || IsCurrentSessionUserDesktopActive())
|
||||
// The SYSTEM helper may now be starting for an elevated application on
|
||||
// Default too. Old services still reject that with secure_input_not_active;
|
||||
// preserve their ordinary-desktop return instead of retrying indefinitely.
|
||||
if (!IsDesktopInputSetupPending(error) ||
|
||||
(error == "secure_input_not_active" &&
|
||||
IsCurrentSessionUserDesktopActive())) {
|
||||
return response;
|
||||
}
|
||||
const auto remaining = pipe_deadline_detail::Remaining(deadline);
|
||||
if (remaining == 0) return response;
|
||||
Sleep((std::min)(DWORD{10}, remaining));
|
||||
|
||||
@@ -137,6 +137,9 @@ class CrossDeskServiceHost {
|
||||
ULONGLONG session_helper_report_state_age_ms_ = 0;
|
||||
ULONGLONG session_helper_report_uptime_ms_ = 0;
|
||||
ULONGLONG secure_input_helper_started_at_tick_ = 0;
|
||||
// Short idle grace after a validated user-desktop input request. This keeps
|
||||
// the SYSTEM helper available for elevated windows such as Task Manager.
|
||||
ULONGLONG user_desktop_input_until_tick_ = 0;
|
||||
ULONGLONG sas_secure_desktop_until_tick_ = 0;
|
||||
bool session_locked_ = false;
|
||||
bool logon_ui_visible_ = false;
|
||||
|
||||
Reference in New Issue
Block a user