mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-08-06 01:59:33 +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 {
|
struct WindowsServiceInteractiveStatus {
|
||||||
bool available = false;
|
bool available = false;
|
||||||
|
bool sas_secure_desktop_grace_active = false;
|
||||||
unsigned int error_code = 0;
|
unsigned int error_code = 0;
|
||||||
std::string interactive_stage;
|
std::string interactive_stage;
|
||||||
std::string error;
|
std::string error;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint32_t kWindowsServiceStatusIntervalMs = 1000;
|
constexpr uint32_t kWindowsServiceStatusIntervalMs = 1000;
|
||||||
|
constexpr uint32_t kWindowsServiceSasSecureDesktopGraceMs = 2000;
|
||||||
constexpr DWORD kWindowsServiceQueryTimeoutMs = 500;
|
constexpr DWORD kWindowsServiceQueryTimeoutMs = 500;
|
||||||
constexpr DWORD kWindowsServiceSasTimeoutMs = 500;
|
constexpr DWORD kWindowsServiceSasTimeoutMs = 500;
|
||||||
|
|
||||||
@@ -130,6 +132,8 @@ bool QueryWindowsServiceInteractiveStatus(
|
|||||||
}
|
}
|
||||||
|
|
||||||
status->interactive_stage = json.value("interactive_stage", std::string());
|
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(
|
if (ShouldNormalizeUnlockToUserDesktop(
|
||||||
json.value("interactive_lock_screen_visible", false),
|
json.value("interactive_lock_screen_visible", false),
|
||||||
@@ -1928,6 +1932,12 @@ void Render::HandleWindowsServiceIntegration() {
|
|||||||
LOG_WARN("Remote SAS request failed: {}", response);
|
LOG_WARN("Remote SAS request failed: {}", response);
|
||||||
} else {
|
} else {
|
||||||
LOG_INFO("Remote SAS request forwarded to local Windows service");
|
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;
|
last_windows_service_status_tick_ = 0;
|
||||||
force_broadcast = true;
|
force_broadcast = true;
|
||||||
@@ -1943,15 +1953,31 @@ void Render::HandleWindowsServiceIntegration() {
|
|||||||
|
|
||||||
WindowsServiceInteractiveStatus status;
|
WindowsServiceInteractiveStatus status;
|
||||||
const bool status_ok = QueryWindowsServiceInteractiveStatus(&status);
|
const bool status_ok = QueryWindowsServiceInteractiveStatus(&status);
|
||||||
|
WindowsServiceInteractiveStatus broadcast_status = status;
|
||||||
const bool previous_secure_desktop_interaction =
|
const bool previous_secure_desktop_interaction =
|
||||||
IsSecureDesktopInteractionRequired(local_interactive_stage_);
|
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_ =
|
local_service_status_received_ =
|
||||||
status_ok || previous_secure_desktop_interaction;
|
status_ok || previous_secure_desktop_interaction;
|
||||||
local_service_available_ = status.available;
|
local_service_available_ = status.available;
|
||||||
if (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) {
|
} else if (!previous_secure_desktop_interaction) {
|
||||||
local_interactive_stage_.clear();
|
local_interactive_stage_.clear();
|
||||||
|
optimistic_windows_secure_desktop_until_tick_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status_ok) {
|
if (status_ok) {
|
||||||
@@ -1990,7 +2016,7 @@ void Render::HandleWindowsServiceIntegration() {
|
|||||||
last_logged_service_error_code = 0;
|
last_logged_service_error_code = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteAction remote_action = BuildWindowsServiceStatusAction(status);
|
RemoteAction remote_action = BuildWindowsServiceStatusAction(broadcast_status);
|
||||||
std::string msg = remote_action.to_json();
|
std::string msg = remote_action.to_json();
|
||||||
int ret = SendReliableDataFrame(peer_, msg.data(), msg.size(),
|
int ret = SendReliableDataFrame(peer_, msg.data(), msg.size(),
|
||||||
control_data_label_.c_str());
|
control_data_label_.c_str());
|
||||||
@@ -2009,6 +2035,7 @@ void Render::ResetLocalWindowsServiceState(bool clear_pending_sas) {
|
|||||||
local_service_status_received_ = false;
|
local_service_status_received_ = false;
|
||||||
local_service_available_ = false;
|
local_service_available_ = false;
|
||||||
local_interactive_stage_.clear();
|
local_interactive_stage_.clear();
|
||||||
|
optimistic_windows_secure_desktop_until_tick_ = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -547,6 +547,7 @@ class Render {
|
|||||||
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;
|
||||||
|
uint32_t optimistic_windows_secure_desktop_until_tick_ = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// stream window render
|
// stream window render
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ constexpr char kSecureDesktopMouseIpcCommandPrefix[] = "secure-input-mouse:";
|
|||||||
constexpr wchar_t kCrossDeskClientProcessName[] = L"crossdesk.exe";
|
constexpr wchar_t kCrossDeskClientProcessName[] = L"crossdesk.exe";
|
||||||
constexpr DWORD kCrossDeskClientMonitorIntervalMs = 1000;
|
constexpr DWORD kCrossDeskClientMonitorIntervalMs = 1000;
|
||||||
constexpr ULONGLONG kCrossDeskClientMonitorStartupGraceMs = 5000;
|
constexpr ULONGLONG kCrossDeskClientMonitorStartupGraceMs = 5000;
|
||||||
|
constexpr ULONGLONG kSasSecureDesktopGraceMs = 15000;
|
||||||
|
|
||||||
using SendSasFunction = VOID(WINAPI*)(BOOL);
|
using SendSasFunction = VOID(WINAPI*)(BOOL);
|
||||||
|
|
||||||
@@ -1027,12 +1028,14 @@ int CrossDeskServiceHost::InitializeRuntime() {
|
|||||||
session_helper_report_credential_ui_visible_ = false;
|
session_helper_report_credential_ui_visible_ = false;
|
||||||
session_helper_report_unlock_ui_visible_ = false;
|
session_helper_report_unlock_ui_visible_ = false;
|
||||||
secure_input_helper_running_ = false;
|
secure_input_helper_running_ = false;
|
||||||
|
sas_secure_desktop_seen_ = false;
|
||||||
last_sas_error_code_ = 0;
|
last_sas_error_code_ = 0;
|
||||||
last_sas_success_ = false;
|
last_sas_success_ = false;
|
||||||
session_helper_started_at_tick_ = 0;
|
session_helper_started_at_tick_ = 0;
|
||||||
session_helper_report_state_age_ms_ = 0;
|
session_helper_report_state_age_ms_ = 0;
|
||||||
session_helper_report_uptime_ms_ = 0;
|
session_helper_report_uptime_ms_ = 0;
|
||||||
secure_input_helper_started_at_tick_ = 0;
|
secure_input_helper_started_at_tick_ = 0;
|
||||||
|
sas_secure_desktop_until_tick_ = 0;
|
||||||
session_helper_process_handle_ = nullptr;
|
session_helper_process_handle_ = nullptr;
|
||||||
session_helper_stop_event_ = nullptr;
|
session_helper_stop_event_ = nullptr;
|
||||||
secure_input_helper_process_handle_ = nullptr;
|
secure_input_helper_process_handle_ = nullptr;
|
||||||
@@ -1320,7 +1323,8 @@ bool CrossDeskServiceHost::IsHelperReportingLockScreenLocked() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CrossDeskServiceHost::HasSecureInputUiLocked() 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_credential_ui_visible_ ||
|
||||||
session_helper_report_secure_desktop_active_ ||
|
session_helper_report_secure_desktop_active_ ||
|
||||||
session_helper_report_unlock_ui_visible_ ||
|
session_helper_report_unlock_ui_visible_ ||
|
||||||
@@ -1328,6 +1332,30 @@ bool CrossDeskServiceHost::HasSecureInputUiLocked() const {
|
|||||||
session_helper_report_interactive_stage_ == "secure-desktop";
|
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(
|
bool CrossDeskServiceHost::ShouldKeepSecureInputHelperLocked(
|
||||||
DWORD target_session_id) const {
|
DWORD target_session_id) const {
|
||||||
if (target_session_id == 0xFFFFFFFF) {
|
if (target_session_id == 0xFFFFFFFF) {
|
||||||
@@ -1339,6 +1367,12 @@ bool CrossDeskServiceHost::ShouldKeepSecureInputHelperLocked(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string CrossDeskServiceHost::ResolveInteractiveStageLocked() const {
|
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()) {
|
if (!session_helper_report_interactive_stage_.empty()) {
|
||||||
return session_helper_report_interactive_stage_;
|
return session_helper_report_interactive_stage_;
|
||||||
}
|
}
|
||||||
@@ -1818,6 +1852,7 @@ void CrossDeskServiceHost::RefreshSessionHelperReportedState() {
|
|||||||
json.value("interactive_stage", std::string());
|
json.value("interactive_stage", std::string());
|
||||||
session_helper_report_state_age_ms_ = json.value("state_age_ms", 0ull);
|
session_helper_report_state_age_ms_ = json.value("state_age_ms", 0ull);
|
||||||
session_helper_report_uptime_ms_ = json.value("uptime_ms", 0ull);
|
session_helper_report_uptime_ms_ = json.value("uptime_ms", 0ull);
|
||||||
|
UpdateSasSecureDesktopGraceLocked(session_helper_report_interactive_stage_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrossDeskServiceHost::RecordSessionEvent(DWORD event_type,
|
void CrossDeskServiceHost::RecordSessionEvent(DWORD event_type,
|
||||||
@@ -1947,6 +1982,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() {
|
|||||||
std::string secure_input_helper_interactive_stage =
|
std::string secure_input_helper_interactive_stage =
|
||||||
EscapeJsonString(secure_input_helper_interactive_stage_);
|
EscapeJsonString(secure_input_helper_interactive_stage_);
|
||||||
bool interactive_state_ready = session_helper_status_ok_;
|
bool interactive_state_ready = session_helper_status_ok_;
|
||||||
|
const bool sas_secure_desktop_grace_active =
|
||||||
|
IsSasSecureDesktopGraceActiveLocked();
|
||||||
const char* interactive_state_source =
|
const char* interactive_state_source =
|
||||||
interactive_state_ready ? "session-helper" : "service-host";
|
interactive_state_ready ? "session-helper" : "service-host";
|
||||||
const bool effective_session_locked = GetEffectiveSessionLockedLocked();
|
const bool effective_session_locked = GetEffectiveSessionLockedLocked();
|
||||||
@@ -1960,21 +1997,23 @@ std::string CrossDeskServiceHost::BuildStatusResponse() {
|
|||||||
bool unlock_ui_visible = interactive_state_ready
|
bool unlock_ui_visible = interactive_state_ready
|
||||||
? session_helper_report_unlock_ui_visible_
|
? session_helper_report_unlock_ui_visible_
|
||||||
: (logon_ui_visible_ || secure_desktop_active_);
|
: (logon_ui_visible_ || secure_desktop_active_);
|
||||||
|
unlock_ui_visible = unlock_ui_visible || sas_secure_desktop_grace_active;
|
||||||
bool interactive_secure_desktop_active =
|
bool interactive_secure_desktop_active =
|
||||||
interactive_state_ready ? session_helper_report_secure_desktop_active_
|
interactive_state_ready ? session_helper_report_secure_desktop_active_
|
||||||
: secure_desktop_active_;
|
: secure_desktop_active_;
|
||||||
|
interactive_secure_desktop_active =
|
||||||
|
interactive_secure_desktop_active || sas_secure_desktop_grace_active;
|
||||||
bool interactive_logon_ui_visible =
|
bool interactive_logon_ui_visible =
|
||||||
interactive_state_ready ? session_helper_report_logon_ui_visible_
|
interactive_state_ready ? session_helper_report_logon_ui_visible_
|
||||||
: logon_ui_visible_;
|
: logon_ui_visible_;
|
||||||
bool interactive_session_locked = effective_session_locked ||
|
bool interactive_session_locked = effective_session_locked ||
|
||||||
interactive_lock_screen_visible ||
|
interactive_lock_screen_visible ||
|
||||||
unlock_ui_visible;
|
unlock_ui_visible ||
|
||||||
|
sas_secure_desktop_grace_active;
|
||||||
std::string interactive_input_desktop = EscapeJsonString(
|
std::string interactive_input_desktop = EscapeJsonString(
|
||||||
interactive_state_ready ? session_helper_report_input_desktop_
|
interactive_state_ready ? session_helper_report_input_desktop_
|
||||||
: input_desktop_name_);
|
: input_desktop_name_);
|
||||||
std::string raw_interactive_stage = DetermineInteractiveStage(
|
std::string raw_interactive_stage = ResolveInteractiveStageLocked();
|
||||||
interactive_lock_screen_visible, credential_ui_visible,
|
|
||||||
interactive_secure_desktop_active);
|
|
||||||
std::string interactive_stage = EscapeJsonString(raw_interactive_stage);
|
std::string interactive_stage = EscapeJsonString(raw_interactive_stage);
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << "{\"ok\":true,\"service\":\"CrossDeskService\""
|
stream << "{\"ok\":true,\"service\":\"CrossDeskService\""
|
||||||
@@ -1996,6 +2035,8 @@ std::string CrossDeskServiceHost::BuildStatusResponse() {
|
|||||||
<< (interactive_logon_ui_visible ? "true" : "false")
|
<< (interactive_logon_ui_visible ? "true" : "false")
|
||||||
<< ",\"interactive_secure_desktop_active\":"
|
<< ",\"interactive_secure_desktop_active\":"
|
||||||
<< (interactive_secure_desktop_active ? "true" : "false")
|
<< (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")
|
<< ",\"unlock_ui_visible\":" << (unlock_ui_visible ? "true" : "false")
|
||||||
<< ",\"credential_ui_visible\":"
|
<< ",\"credential_ui_visible\":"
|
||||||
<< (credential_ui_visible ? "true" : "false")
|
<< (credential_ui_visible ? "true" : "false")
|
||||||
@@ -2100,10 +2141,14 @@ std::string CrossDeskServiceHost::SendSecureAttentionSequence() {
|
|||||||
SasResult result = SendSasNow();
|
SasResult result = SendSasNow();
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(state_mutex_);
|
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_success_ = result.success;
|
||||||
last_sas_error_code_ = result.error_code;
|
last_sas_error_code_ = result.error_code;
|
||||||
last_sas_error_ = result.error;
|
last_sas_error_ = result.error;
|
||||||
|
sas_secure_desktop_until_tick_ =
|
||||||
|
result.success ? now + kSasSecureDesktopGraceMs : 0;
|
||||||
|
sas_secure_desktop_seen_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ class CrossDeskServiceHost {
|
|||||||
bool GetEffectiveSessionLockedLocked() const;
|
bool GetEffectiveSessionLockedLocked() const;
|
||||||
bool IsHelperReportingLockScreenLocked() const;
|
bool IsHelperReportingLockScreenLocked() const;
|
||||||
bool HasSecureInputUiLocked() const;
|
bool HasSecureInputUiLocked() const;
|
||||||
|
void UpdateSasSecureDesktopGraceLocked(const std::string& observed_stage);
|
||||||
|
bool IsSasSecureDesktopGraceActiveLocked() const;
|
||||||
bool ShouldKeepSecureInputHelperLocked(DWORD target_session_id) const;
|
bool ShouldKeepSecureInputHelperLocked(DWORD target_session_id) const;
|
||||||
std::string ResolveInteractiveStageLocked() const;
|
std::string ResolveInteractiveStageLocked() const;
|
||||||
void RefreshSessionHelperReportedState();
|
void RefreshSessionHelperReportedState();
|
||||||
@@ -103,6 +105,7 @@ class CrossDeskServiceHost {
|
|||||||
ULONGLONG session_helper_report_state_age_ms_ = 0;
|
ULONGLONG session_helper_report_state_age_ms_ = 0;
|
||||||
ULONGLONG session_helper_report_uptime_ms_ = 0;
|
ULONGLONG session_helper_report_uptime_ms_ = 0;
|
||||||
ULONGLONG secure_input_helper_started_at_tick_ = 0;
|
ULONGLONG secure_input_helper_started_at_tick_ = 0;
|
||||||
|
ULONGLONG sas_secure_desktop_until_tick_ = 0;
|
||||||
bool session_locked_ = false;
|
bool session_locked_ = false;
|
||||||
bool logon_ui_visible_ = false;
|
bool logon_ui_visible_ = false;
|
||||||
bool prelogin_ = false;
|
bool prelogin_ = false;
|
||||||
@@ -119,6 +122,7 @@ class CrossDeskServiceHost {
|
|||||||
bool session_helper_report_unlock_ui_visible_ = false;
|
bool session_helper_report_unlock_ui_visible_ = false;
|
||||||
bool secure_input_helper_running_ = false;
|
bool secure_input_helper_running_ = false;
|
||||||
bool console_mode_ = false;
|
bool console_mode_ = false;
|
||||||
|
bool sas_secure_desktop_seen_ = false;
|
||||||
DWORD last_sas_error_code_ = 0;
|
DWORD last_sas_error_code_ = 0;
|
||||||
bool last_sas_success_ = false;
|
bool last_sas_success_ = false;
|
||||||
HANDLE session_helper_process_handle_ = nullptr;
|
HANDLE session_helper_process_handle_ = nullptr;
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ using crossdesk::get_logger;
|
|||||||
using crossdesk::InitLogger;
|
using crossdesk::InitLogger;
|
||||||
using Json = nlohmann::json;
|
using Json = nlohmann::json;
|
||||||
|
|
||||||
|
constexpr DWORD kSessionHelperStatePollMs = 1000;
|
||||||
|
|
||||||
|
std::atomic<HANDLE> g_session_helper_desktop_switch_event{nullptr};
|
||||||
|
|
||||||
struct InputDesktopInfo {
|
struct InputDesktopInfo {
|
||||||
bool available = false;
|
bool available = false;
|
||||||
DWORD error_code = 0;
|
DWORD error_code = 0;
|
||||||
@@ -134,6 +138,54 @@ void EnablePerMonitorDpiAwareness() {
|
|||||||
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
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) {
|
std::wstring Utf8ToWide(const std::string& value) {
|
||||||
if (value.empty()) {
|
if (value.empty()) {
|
||||||
return {};
|
return {};
|
||||||
@@ -308,8 +360,13 @@ void UpdateHelperState(HelperState* helper_state) {
|
|||||||
IsLockAppRunningInCurrentSession(helper_state->session_id);
|
IsLockAppRunningInCurrentSession(helper_state->session_id);
|
||||||
bool logon_ui_visible =
|
bool logon_ui_visible =
|
||||||
IsLogonUiRunningInCurrentSession(helper_state->session_id);
|
IsLogonUiRunningInCurrentSession(helper_state->session_id);
|
||||||
const bool secure_desktop_active =
|
const bool input_desktop_is_winlogon =
|
||||||
_stricmp(desktop_info.name.c_str(), "Winlogon") == 0;
|
_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;
|
bool session_locked = false;
|
||||||
if (!QuerySessionLockState(helper_state->session_id, &session_locked)) {
|
if (!QuerySessionLockState(helper_state->session_id, &session_locked)) {
|
||||||
session_locked =
|
session_locked =
|
||||||
@@ -1796,6 +1853,26 @@ int main(int argc, char* argv[]) {
|
|||||||
helper_state.started_at_tick = GetTickCount64();
|
helper_state.started_at_tick = GetTickCount64();
|
||||||
UpdateHelperState(&helper_state);
|
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,
|
std::thread ipc_thread(HelperIpcServerLoop, stop_event, current_session_id,
|
||||||
&helper_state);
|
&helper_state);
|
||||||
|
|
||||||
@@ -1847,13 +1924,17 @@ int main(int argc, char* argv[]) {
|
|||||||
last_stage = stage;
|
last_stage = stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD wait_result = stop_event != nullptr
|
DWORD wait_result =
|
||||||
? WaitForSingleObject(stop_event, 1000)
|
WaitForSessionHelperStateChange(stop_event, desktop_switch_event);
|
||||||
: WAIT_TIMEOUT;
|
if (stop_event != nullptr && wait_result == WAIT_OBJECT_0) {
|
||||||
if (wait_result == WAIT_OBJECT_0) {
|
|
||||||
break;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1862,6 +1943,15 @@ int main(int argc, char* argv[]) {
|
|||||||
ipc_thread.join();
|
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) {
|
if (stop_event != nullptr) {
|
||||||
CloseHandle(stop_event);
|
CloseHandle(stop_event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "interactive_state.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::filesystem::path FindRepoRoot() {
|
||||||
|
std::filesystem::path current = std::filesystem::current_path();
|
||||||
|
while (!current.empty()) {
|
||||||
|
if (std::filesystem::exists(current / "xmake.lua") &&
|
||||||
|
std::filesystem::exists(
|
||||||
|
current / "src/service/windows/service_host.cpp")) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
current = current.parent_path();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ReadFile(const std::filesystem::path& path) {
|
||||||
|
std::ifstream file(path, std::ios::binary);
|
||||||
|
if (!file) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << file.rdbuf();
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpectContains(const char* name, const std::string& value,
|
||||||
|
const std::string& expected) {
|
||||||
|
if (value.find(expected) != std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << name << " missing expected text: " << expected << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpectNotContains(const char* name, const std::string& value,
|
||||||
|
const std::string& unexpected) {
|
||||||
|
if (value.find(unexpected) == std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << name << " contains unexpected text: " << unexpected << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpectTrue(const char* name, bool value) {
|
||||||
|
if (value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << name << " expected true\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const std::filesystem::path repo_root = FindRepoRoot();
|
||||||
|
if (repo_root.empty()) {
|
||||||
|
std::cerr << "failed to locate repository root\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string control_bar =
|
||||||
|
ReadFile(repo_root / "src/gui/toolbars/control_bar.cpp");
|
||||||
|
const std::string render = ReadFile(repo_root / "src/gui/render.cpp");
|
||||||
|
const std::string render_h = ReadFile(repo_root / "src/gui/render.h");
|
||||||
|
const std::string service_host =
|
||||||
|
ReadFile(repo_root / "src/service/windows/service_host.cpp");
|
||||||
|
const std::string service_host_h =
|
||||||
|
ReadFile(repo_root / "src/service/windows/service_host.h");
|
||||||
|
const std::string session_helper =
|
||||||
|
ReadFile(repo_root / "src/service/windows/session_helper_main.cpp");
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
ok &= ExpectTrue("secure desktop input routing",
|
||||||
|
crossdesk::IsSecureDesktopInteractionRequired(
|
||||||
|
"secure-desktop"));
|
||||||
|
ok &= ExpectNotContains("control_bar.cpp", control_bar,
|
||||||
|
"CanSendSecureAttentionSequence("
|
||||||
|
"props->remote_interactive_stage_)");
|
||||||
|
ok &= ExpectNotContains("control_bar.cpp", control_bar,
|
||||||
|
"ImGui::BeginDisabled();\n"
|
||||||
|
" }\n"
|
||||||
|
" if (ImGui::Selectable(sas_label.c_str()))");
|
||||||
|
ok &= ExpectNotContains("render.cpp", render, "sas_requires_lock_screen");
|
||||||
|
ok &= ExpectContains("render.h", render_h,
|
||||||
|
"optimistic_windows_secure_desktop_until_tick_");
|
||||||
|
ok &= ExpectContains("render.cpp", render,
|
||||||
|
"kWindowsServiceSasSecureDesktopGraceMs");
|
||||||
|
ok &= ExpectContains("render.cpp", render,
|
||||||
|
"status->sas_secure_desktop_grace_active");
|
||||||
|
ok &= ExpectContains("render.cpp", render,
|
||||||
|
"json.value(\"sas_secure_desktop_grace_active\", false)");
|
||||||
|
ok &= ExpectContains("render.cpp", render,
|
||||||
|
"status.sas_secure_desktop_grace_active");
|
||||||
|
ok &= ExpectContains("render.cpp", render,
|
||||||
|
"local_interactive_stage_ = \"secure-desktop\"");
|
||||||
|
ok &= ExpectContains("service_host.h", service_host_h,
|
||||||
|
"sas_secure_desktop_until_tick_");
|
||||||
|
ok &= ExpectContains("service_host.h", service_host_h,
|
||||||
|
"sas_secure_desktop_seen_");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"kSasSecureDesktopGraceMs");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"IsSasSecureDesktopGraceActiveLocked()");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"UpdateSasSecureDesktopGraceLocked("
|
||||||
|
"session_helper_report_interactive_stage_)");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"sas_secure_desktop_seen_ = true");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"sas_secure_desktop_until_tick_ = 0");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"sas_secure_desktop_until_tick_ =");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"now + kSasSecureDesktopGraceMs");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"\\\"sas_secure_desktop_grace_active\\\"");
|
||||||
|
ok &= ExpectContains("service_host.cpp", service_host,
|
||||||
|
"raw_interactive_stage = ResolveInteractiveStageLocked()");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"kSessionHelperStatePollMs = 1000");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"EVENT_SYSTEM_DESKTOPSWITCH");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"SetWinEventHook(");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"MsgWaitForMultipleObjects");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"WaitForSessionHelperStateChange(stop_event, "
|
||||||
|
"desktop_switch_event)");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"inaccessible_secure_input_desktop");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"desktop_info.error_code == ERROR_ACCESS_DENIED");
|
||||||
|
ok &= ExpectContains("session_helper_main.cpp", session_helper,
|
||||||
|
"secure_desktop_active = input_desktop_is_winlogon ||");
|
||||||
|
return ok ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -54,6 +54,12 @@ function setup_targets()
|
|||||||
set_default(false)
|
set_default(false)
|
||||||
add_files("tests/windows_mouse_controller_safety_test.cpp")
|
add_files("tests/windows_mouse_controller_safety_test.cpp")
|
||||||
|
|
||||||
|
target("windows_sas_guard_test")
|
||||||
|
set_kind("binary")
|
||||||
|
set_default(false)
|
||||||
|
add_includedirs("src/service/windows")
|
||||||
|
add_files("tests/windows_sas_guard_test.cpp")
|
||||||
|
|
||||||
target("display_popup_hover_state_test")
|
target("display_popup_hover_state_test")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
set_default(false)
|
set_default(false)
|
||||||
|
|||||||
Reference in New Issue
Block a user