mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-06-11 01:44:50 +08:00
[fix] handle SAS secure desktop transitions and restore desktop capture promptly, refs #77
This commit is contained in:
+29
-2
@@ -82,12 +82,14 @@ HICON LoadTrayIcon() {
|
||||
|
||||
struct WindowsServiceInteractiveStatus {
|
||||
bool available = false;
|
||||
bool sas_secure_desktop_grace_active = false;
|
||||
unsigned int error_code = 0;
|
||||
std::string interactive_stage;
|
||||
std::string error;
|
||||
};
|
||||
|
||||
constexpr uint32_t kWindowsServiceStatusIntervalMs = 1000;
|
||||
constexpr uint32_t kWindowsServiceSasSecureDesktopGraceMs = 2000;
|
||||
constexpr DWORD kWindowsServiceQueryTimeoutMs = 500;
|
||||
constexpr DWORD kWindowsServiceSasTimeoutMs = 500;
|
||||
|
||||
@@ -130,6 +132,8 @@ bool QueryWindowsServiceInteractiveStatus(
|
||||
}
|
||||
|
||||
status->interactive_stage = json.value("interactive_stage", std::string());
|
||||
status->sas_secure_desktop_grace_active =
|
||||
json.value("sas_secure_desktop_grace_active", false);
|
||||
|
||||
if (ShouldNormalizeUnlockToUserDesktop(
|
||||
json.value("interactive_lock_screen_visible", false),
|
||||
@@ -1928,6 +1932,12 @@ void Render::HandleWindowsServiceIntegration() {
|
||||
LOG_WARN("Remote SAS request failed: {}", response);
|
||||
} else {
|
||||
LOG_INFO("Remote SAS request forwarded to local Windows service");
|
||||
optimistic_windows_secure_desktop_until_tick_ =
|
||||
static_cast<uint32_t>(SDL_GetTicks()) +
|
||||
kWindowsServiceSasSecureDesktopGraceMs;
|
||||
local_service_status_received_ = true;
|
||||
local_service_available_ = true;
|
||||
local_interactive_stage_ = "secure-desktop";
|
||||
}
|
||||
last_windows_service_status_tick_ = 0;
|
||||
force_broadcast = true;
|
||||
@@ -1943,15 +1953,31 @@ void Render::HandleWindowsServiceIntegration() {
|
||||
|
||||
WindowsServiceInteractiveStatus status;
|
||||
const bool status_ok = QueryWindowsServiceInteractiveStatus(&status);
|
||||
WindowsServiceInteractiveStatus broadcast_status = status;
|
||||
const bool previous_secure_desktop_interaction =
|
||||
IsSecureDesktopInteractionRequired(local_interactive_stage_);
|
||||
const bool optimistic_secure_desktop_active =
|
||||
optimistic_windows_secure_desktop_until_tick_ != 0 &&
|
||||
static_cast<int32_t>(optimistic_windows_secure_desktop_until_tick_ -
|
||||
now) > 0;
|
||||
const bool keep_optimistic_secure_desktop =
|
||||
status_ok && status.available && optimistic_secure_desktop_active &&
|
||||
status.sas_secure_desktop_grace_active &&
|
||||
status.interactive_stage == "user-desktop";
|
||||
local_service_status_received_ =
|
||||
status_ok || previous_secure_desktop_interaction;
|
||||
local_service_available_ = status.available;
|
||||
if (status.available) {
|
||||
local_interactive_stage_ = status.interactive_stage;
|
||||
if (keep_optimistic_secure_desktop) {
|
||||
local_interactive_stage_ = "secure-desktop";
|
||||
broadcast_status.interactive_stage = local_interactive_stage_;
|
||||
} else {
|
||||
local_interactive_stage_ = status.interactive_stage;
|
||||
optimistic_windows_secure_desktop_until_tick_ = 0;
|
||||
}
|
||||
} else if (!previous_secure_desktop_interaction) {
|
||||
local_interactive_stage_.clear();
|
||||
optimistic_windows_secure_desktop_until_tick_ = 0;
|
||||
}
|
||||
|
||||
if (status_ok) {
|
||||
@@ -1990,7 +2016,7 @@ void Render::HandleWindowsServiceIntegration() {
|
||||
last_logged_service_error_code = 0;
|
||||
}
|
||||
|
||||
RemoteAction remote_action = BuildWindowsServiceStatusAction(status);
|
||||
RemoteAction remote_action = BuildWindowsServiceStatusAction(broadcast_status);
|
||||
std::string msg = remote_action.to_json();
|
||||
int ret = SendReliableDataFrame(peer_, msg.data(), msg.size(),
|
||||
control_data_label_.c_str());
|
||||
@@ -2009,6 +2035,7 @@ void Render::ResetLocalWindowsServiceState(bool clear_pending_sas) {
|
||||
local_service_status_received_ = false;
|
||||
local_service_available_ = false;
|
||||
local_interactive_stage_.clear();
|
||||
optimistic_windows_secure_desktop_until_tick_ = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -547,6 +547,7 @@ class Render {
|
||||
std::string local_interactive_stage_;
|
||||
uint32_t last_local_secure_input_block_log_tick_ = 0;
|
||||
uint32_t last_windows_service_status_tick_ = 0;
|
||||
uint32_t optimistic_windows_secure_desktop_until_tick_ = 0;
|
||||
#endif
|
||||
|
||||
// stream window render
|
||||
|
||||
@@ -31,6 +31,7 @@ constexpr char kSecureDesktopMouseIpcCommandPrefix[] = "secure-input-mouse:";
|
||||
constexpr wchar_t kCrossDeskClientProcessName[] = L"crossdesk.exe";
|
||||
constexpr DWORD kCrossDeskClientMonitorIntervalMs = 1000;
|
||||
constexpr ULONGLONG kCrossDeskClientMonitorStartupGraceMs = 5000;
|
||||
constexpr ULONGLONG kSasSecureDesktopGraceMs = 15000;
|
||||
|
||||
using SendSasFunction = VOID(WINAPI*)(BOOL);
|
||||
|
||||
@@ -1027,12 +1028,14 @@ int CrossDeskServiceHost::InitializeRuntime() {
|
||||
session_helper_report_credential_ui_visible_ = false;
|
||||
session_helper_report_unlock_ui_visible_ = false;
|
||||
secure_input_helper_running_ = false;
|
||||
sas_secure_desktop_seen_ = false;
|
||||
last_sas_error_code_ = 0;
|
||||
last_sas_success_ = false;
|
||||
session_helper_started_at_tick_ = 0;
|
||||
session_helper_report_state_age_ms_ = 0;
|
||||
session_helper_report_uptime_ms_ = 0;
|
||||
secure_input_helper_started_at_tick_ = 0;
|
||||
sas_secure_desktop_until_tick_ = 0;
|
||||
session_helper_process_handle_ = nullptr;
|
||||
session_helper_stop_event_ = nullptr;
|
||||
secure_input_helper_process_handle_ = nullptr;
|
||||
@@ -1320,7 +1323,8 @@ bool CrossDeskServiceHost::IsHelperReportingLockScreenLocked() const {
|
||||
}
|
||||
|
||||
bool CrossDeskServiceHost::HasSecureInputUiLocked() const {
|
||||
return prelogin_ || secure_desktop_active_ || logon_ui_visible_ ||
|
||||
return IsSasSecureDesktopGraceActiveLocked() || prelogin_ ||
|
||||
secure_desktop_active_ || logon_ui_visible_ ||
|
||||
session_helper_report_credential_ui_visible_ ||
|
||||
session_helper_report_secure_desktop_active_ ||
|
||||
session_helper_report_unlock_ui_visible_ ||
|
||||
@@ -1328,6 +1332,30 @@ bool CrossDeskServiceHost::HasSecureInputUiLocked() const {
|
||||
session_helper_report_interactive_stage_ == "secure-desktop";
|
||||
}
|
||||
|
||||
void CrossDeskServiceHost::UpdateSasSecureDesktopGraceLocked(
|
||||
const std::string& observed_stage) {
|
||||
if (sas_secure_desktop_until_tick_ == 0) {
|
||||
sas_secure_desktop_seen_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (observed_stage == "credential-ui" || observed_stage == "secure-desktop" ||
|
||||
observed_stage == "lock-screen") {
|
||||
sas_secure_desktop_seen_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sas_secure_desktop_seen_ && observed_stage == "user-desktop") {
|
||||
sas_secure_desktop_until_tick_ = 0;
|
||||
sas_secure_desktop_seen_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CrossDeskServiceHost::IsSasSecureDesktopGraceActiveLocked() const {
|
||||
return last_sas_success_ && sas_secure_desktop_until_tick_ != 0 &&
|
||||
GetTickCount64() < sas_secure_desktop_until_tick_;
|
||||
}
|
||||
|
||||
bool CrossDeskServiceHost::ShouldKeepSecureInputHelperLocked(
|
||||
DWORD target_session_id) const {
|
||||
if (target_session_id == 0xFFFFFFFF) {
|
||||
@@ -1339,6 +1367,12 @@ bool CrossDeskServiceHost::ShouldKeepSecureInputHelperLocked(
|
||||
}
|
||||
|
||||
std::string CrossDeskServiceHost::ResolveInteractiveStageLocked() const {
|
||||
if (IsSasSecureDesktopGraceActiveLocked() &&
|
||||
(session_helper_report_interactive_stage_.empty() ||
|
||||
session_helper_report_interactive_stage_ == "user-desktop")) {
|
||||
return "secure-desktop";
|
||||
}
|
||||
|
||||
if (!session_helper_report_interactive_stage_.empty()) {
|
||||
return session_helper_report_interactive_stage_;
|
||||
}
|
||||
@@ -1818,6 +1852,7 @@ void CrossDeskServiceHost::RefreshSessionHelperReportedState() {
|
||||
json.value("interactive_stage", std::string());
|
||||
session_helper_report_state_age_ms_ = json.value("state_age_ms", 0ull);
|
||||
session_helper_report_uptime_ms_ = json.value("uptime_ms", 0ull);
|
||||
UpdateSasSecureDesktopGraceLocked(session_helper_report_interactive_stage_);
|
||||
}
|
||||
|
||||
void CrossDeskServiceHost::RecordSessionEvent(DWORD event_type,
|
||||
@@ -1947,6 +1982,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() {
|
||||
std::string secure_input_helper_interactive_stage =
|
||||
EscapeJsonString(secure_input_helper_interactive_stage_);
|
||||
bool interactive_state_ready = session_helper_status_ok_;
|
||||
const bool sas_secure_desktop_grace_active =
|
||||
IsSasSecureDesktopGraceActiveLocked();
|
||||
const char* interactive_state_source =
|
||||
interactive_state_ready ? "session-helper" : "service-host";
|
||||
const bool effective_session_locked = GetEffectiveSessionLockedLocked();
|
||||
@@ -1960,21 +1997,23 @@ std::string CrossDeskServiceHost::BuildStatusResponse() {
|
||||
bool unlock_ui_visible = interactive_state_ready
|
||||
? session_helper_report_unlock_ui_visible_
|
||||
: (logon_ui_visible_ || secure_desktop_active_);
|
||||
unlock_ui_visible = unlock_ui_visible || sas_secure_desktop_grace_active;
|
||||
bool interactive_secure_desktop_active =
|
||||
interactive_state_ready ? session_helper_report_secure_desktop_active_
|
||||
: secure_desktop_active_;
|
||||
interactive_secure_desktop_active =
|
||||
interactive_secure_desktop_active || sas_secure_desktop_grace_active;
|
||||
bool interactive_logon_ui_visible =
|
||||
interactive_state_ready ? session_helper_report_logon_ui_visible_
|
||||
: logon_ui_visible_;
|
||||
bool interactive_session_locked = effective_session_locked ||
|
||||
interactive_lock_screen_visible ||
|
||||
unlock_ui_visible;
|
||||
unlock_ui_visible ||
|
||||
sas_secure_desktop_grace_active;
|
||||
std::string interactive_input_desktop = EscapeJsonString(
|
||||
interactive_state_ready ? session_helper_report_input_desktop_
|
||||
: input_desktop_name_);
|
||||
std::string raw_interactive_stage = DetermineInteractiveStage(
|
||||
interactive_lock_screen_visible, credential_ui_visible,
|
||||
interactive_secure_desktop_active);
|
||||
std::string raw_interactive_stage = ResolveInteractiveStageLocked();
|
||||
std::string interactive_stage = EscapeJsonString(raw_interactive_stage);
|
||||
std::ostringstream stream;
|
||||
stream << "{\"ok\":true,\"service\":\"CrossDeskService\""
|
||||
@@ -1996,6 +2035,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() {
|
||||
<< (interactive_logon_ui_visible ? "true" : "false")
|
||||
<< ",\"interactive_secure_desktop_active\":"
|
||||
<< (interactive_secure_desktop_active ? "true" : "false")
|
||||
<< ",\"sas_secure_desktop_grace_active\":"
|
||||
<< (sas_secure_desktop_grace_active ? "true" : "false")
|
||||
<< ",\"unlock_ui_visible\":" << (unlock_ui_visible ? "true" : "false")
|
||||
<< ",\"credential_ui_visible\":"
|
||||
<< (credential_ui_visible ? "true" : "false")
|
||||
@@ -2100,10 +2141,14 @@ std::string CrossDeskServiceHost::SendSecureAttentionSequence() {
|
||||
SasResult result = SendSasNow();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||
last_sas_tick_ = GetTickCount64();
|
||||
const ULONGLONG now = GetTickCount64();
|
||||
last_sas_tick_ = now;
|
||||
last_sas_success_ = result.success;
|
||||
last_sas_error_code_ = result.error_code;
|
||||
last_sas_error_ = result.error;
|
||||
sas_secure_desktop_until_tick_ =
|
||||
result.success ? now + kSasSecureDesktopGraceMs : 0;
|
||||
sas_secure_desktop_seen_ = false;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
|
||||
@@ -56,6 +56,8 @@ class CrossDeskServiceHost {
|
||||
bool GetEffectiveSessionLockedLocked() const;
|
||||
bool IsHelperReportingLockScreenLocked() const;
|
||||
bool HasSecureInputUiLocked() const;
|
||||
void UpdateSasSecureDesktopGraceLocked(const std::string& observed_stage);
|
||||
bool IsSasSecureDesktopGraceActiveLocked() const;
|
||||
bool ShouldKeepSecureInputHelperLocked(DWORD target_session_id) const;
|
||||
std::string ResolveInteractiveStageLocked() const;
|
||||
void RefreshSessionHelperReportedState();
|
||||
@@ -103,6 +105,7 @@ 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;
|
||||
ULONGLONG sas_secure_desktop_until_tick_ = 0;
|
||||
bool session_locked_ = false;
|
||||
bool logon_ui_visible_ = false;
|
||||
bool prelogin_ = false;
|
||||
@@ -119,6 +122,7 @@ class CrossDeskServiceHost {
|
||||
bool session_helper_report_unlock_ui_visible_ = false;
|
||||
bool secure_input_helper_running_ = false;
|
||||
bool console_mode_ = false;
|
||||
bool sas_secure_desktop_seen_ = false;
|
||||
DWORD last_sas_error_code_ = 0;
|
||||
bool last_sas_success_ = false;
|
||||
HANDLE session_helper_process_handle_ = nullptr;
|
||||
|
||||
@@ -31,6 +31,10 @@ using crossdesk::get_logger;
|
||||
using crossdesk::InitLogger;
|
||||
using Json = nlohmann::json;
|
||||
|
||||
constexpr DWORD kSessionHelperStatePollMs = 1000;
|
||||
|
||||
std::atomic<HANDLE> g_session_helper_desktop_switch_event{nullptr};
|
||||
|
||||
struct InputDesktopInfo {
|
||||
bool available = false;
|
||||
DWORD error_code = 0;
|
||||
@@ -134,6 +138,54 @@ void EnablePerMonitorDpiAwareness() {
|
||||
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
}
|
||||
|
||||
void CALLBACK SessionHelperDesktopSwitchWinEventProc(
|
||||
HWINEVENTHOOK, DWORD event, HWND, LONG, LONG, DWORD, DWORD) {
|
||||
if (event != EVENT_SYSTEM_DESKTOPSWITCH) {
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE event_handle =
|
||||
g_session_helper_desktop_switch_event.load(std::memory_order_relaxed);
|
||||
if (event_handle != nullptr) {
|
||||
SetEvent(event_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureSessionHelperMessageQueue() {
|
||||
MSG message{};
|
||||
PeekMessageW(&message, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
|
||||
}
|
||||
|
||||
void PumpSessionHelperMessages() {
|
||||
MSG message{};
|
||||
while (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&message);
|
||||
DispatchMessageW(&message);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WaitForSessionHelperStateChange(HANDLE stop_event,
|
||||
HANDLE desktop_switch_event) {
|
||||
std::vector<HANDLE> wait_handles;
|
||||
if (stop_event != nullptr) {
|
||||
wait_handles.push_back(stop_event);
|
||||
}
|
||||
if (desktop_switch_event != nullptr) {
|
||||
wait_handles.push_back(desktop_switch_event);
|
||||
}
|
||||
|
||||
const DWORD handle_count = static_cast<DWORD>(wait_handles.size());
|
||||
const DWORD wait_result = MsgWaitForMultipleObjects(
|
||||
handle_count, wait_handles.empty() ? nullptr : wait_handles.data(), FALSE,
|
||||
kSessionHelperStatePollMs, QS_ALLINPUT);
|
||||
if (wait_result == WAIT_OBJECT_0 + handle_count) {
|
||||
PumpSessionHelperMessages();
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
return wait_result;
|
||||
}
|
||||
|
||||
std::wstring Utf8ToWide(const std::string& value) {
|
||||
if (value.empty()) {
|
||||
return {};
|
||||
@@ -308,8 +360,13 @@ void UpdateHelperState(HelperState* helper_state) {
|
||||
IsLockAppRunningInCurrentSession(helper_state->session_id);
|
||||
bool logon_ui_visible =
|
||||
IsLogonUiRunningInCurrentSession(helper_state->session_id);
|
||||
const bool secure_desktop_active =
|
||||
const bool input_desktop_is_winlogon =
|
||||
_stricmp(desktop_info.name.c_str(), "Winlogon") == 0;
|
||||
const bool inaccessible_secure_input_desktop =
|
||||
!desktop_info.available &&
|
||||
desktop_info.error_code == ERROR_ACCESS_DENIED && !logon_ui_visible;
|
||||
const bool secure_desktop_active = input_desktop_is_winlogon ||
|
||||
inaccessible_secure_input_desktop;
|
||||
bool session_locked = false;
|
||||
if (!QuerySessionLockState(helper_state->session_id, &session_locked)) {
|
||||
session_locked =
|
||||
@@ -1796,6 +1853,26 @@ int main(int argc, char* argv[]) {
|
||||
helper_state.started_at_tick = GetTickCount64();
|
||||
UpdateHelperState(&helper_state);
|
||||
|
||||
EnsureSessionHelperMessageQueue();
|
||||
HANDLE desktop_switch_event = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||
HWINEVENTHOOK desktop_switch_hook = nullptr;
|
||||
if (desktop_switch_event != nullptr) {
|
||||
g_session_helper_desktop_switch_event.store(
|
||||
desktop_switch_event, std::memory_order_relaxed);
|
||||
desktop_switch_hook = SetWinEventHook(
|
||||
EVENT_SYSTEM_DESKTOPSWITCH, EVENT_SYSTEM_DESKTOPSWITCH, nullptr,
|
||||
SessionHelperDesktopSwitchWinEventProc, 0, 0,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
||||
if (desktop_switch_hook == nullptr) {
|
||||
LOG_WARN(
|
||||
"SetWinEventHook(EVENT_SYSTEM_DESKTOPSWITCH) failed, error={}",
|
||||
GetLastError());
|
||||
}
|
||||
} else {
|
||||
LOG_WARN("CreateEventW failed for desktop switch watcher, error={}",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
std::thread ipc_thread(HelperIpcServerLoop, stop_event, current_session_id,
|
||||
&helper_state);
|
||||
|
||||
@@ -1847,13 +1924,17 @@ int main(int argc, char* argv[]) {
|
||||
last_stage = stage;
|
||||
}
|
||||
|
||||
DWORD wait_result = stop_event != nullptr
|
||||
? WaitForSingleObject(stop_event, 1000)
|
||||
: WAIT_TIMEOUT;
|
||||
if (wait_result == WAIT_OBJECT_0) {
|
||||
DWORD wait_result =
|
||||
WaitForSessionHelperStateChange(stop_event, desktop_switch_event);
|
||||
if (stop_event != nullptr && wait_result == WAIT_OBJECT_0) {
|
||||
break;
|
||||
}
|
||||
if (wait_result != WAIT_TIMEOUT && wait_result != WAIT_FAILED) {
|
||||
if (wait_result == WAIT_FAILED) {
|
||||
LOG_WARN("Session helper wait failed, error={}", GetLastError());
|
||||
break;
|
||||
}
|
||||
if (wait_result != WAIT_TIMEOUT && wait_result != WAIT_OBJECT_0 &&
|
||||
wait_result != WAIT_OBJECT_0 + 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1862,6 +1943,15 @@ int main(int argc, char* argv[]) {
|
||||
ipc_thread.join();
|
||||
}
|
||||
|
||||
if (desktop_switch_hook != nullptr) {
|
||||
UnhookWinEvent(desktop_switch_hook);
|
||||
}
|
||||
g_session_helper_desktop_switch_event.store(nullptr,
|
||||
std::memory_order_relaxed);
|
||||
if (desktop_switch_event != nullptr) {
|
||||
CloseHandle(desktop_switch_event);
|
||||
}
|
||||
|
||||
if (stop_event != nullptr) {
|
||||
CloseHandle(stop_event);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user