[feat] add Windows secure desktop remote unlock support for locked sessions, refs #77

This commit is contained in:
dijunkun
2026-04-21 04:10:08 +08:00
parent e4dfb61509
commit ffa94986d5
17 changed files with 4712 additions and 40 deletions
+35
View File
@@ -0,0 +1,35 @@
#ifndef _CROSSDESK_INTERACTIVE_STATE_H_
#define _CROSSDESK_INTERACTIVE_STATE_H_
#include <string>
namespace crossdesk {
inline bool IsSecureDesktopInteractionRequired(
const std::string& interactive_stage) {
return interactive_stage == "credential-ui" ||
interactive_stage == "secure-desktop";
}
inline bool ShouldNormalizeUnlockToUserDesktop(
bool interactive_lock_screen_visible,
const std::string& interactive_stage, bool session_locked,
bool interactive_logon_ui_visible, bool interactive_secure_desktop_active,
bool credential_ui_visible, bool password_box_visible,
bool unlock_ui_visible, const std::string& last_session_event) {
if (!interactive_lock_screen_visible && interactive_stage != "lock-screen") {
return false;
}
if (session_locked || interactive_logon_ui_visible ||
interactive_secure_desktop_active || credential_ui_visible ||
password_box_visible || unlock_ui_visible) {
return false;
}
return last_session_event != "session-lock";
}
} // namespace crossdesk
#endif
+87
View File
@@ -0,0 +1,87 @@
#include <Windows.h>
#include <iostream>
#include <string>
#include "service_host.h"
namespace {
std::wstring GetExecutablePath() {
wchar_t path[MAX_PATH] = {0};
DWORD length = GetModuleFileNameW(nullptr, path, MAX_PATH);
if (length == 0 || length >= MAX_PATH) {
return L"";
}
return std::wstring(path, length);
}
void PrintUsage() {
std::cout << "CrossDesk Windows service skeleton\n"
<< " --service Run under the Windows Service Control Manager\n"
<< " --console Run the service loop in console mode\n"
<< " --install Install the service for the current executable\n"
<< " --uninstall Remove the installed service\n"
<< " --start Start the installed service\n"
<< " --stop Stop the installed service\n"
<< " --sas Ask the service to send Secure Attention Sequence\n"
<< " --ping Ping the running service over named pipe IPC\n"
<< " --status Query runtime status over named pipe IPC\n";
}
} // namespace
int main(int argc, char* argv[]) {
crossdesk::CrossDeskServiceHost host;
if (argc <= 1) {
PrintUsage();
return 0;
}
std::string command = argv[1];
if (command == "--service") {
return host.RunAsService();
}
if (command == "--console") {
return host.RunInConsole();
}
if (command == "--install") {
std::wstring executable_path = GetExecutablePath();
bool success = !executable_path.empty() &&
crossdesk::InstallCrossDeskService(executable_path);
std::cout << (success ? "install ok" : "install failed") << std::endl;
return success ? 0 : 1;
}
if (command == "--uninstall") {
bool success = crossdesk::UninstallCrossDeskService();
std::cout << (success ? "uninstall ok" : "uninstall failed")
<< std::endl;
return success ? 0 : 1;
}
if (command == "--start") {
bool success = crossdesk::StartCrossDeskService();
std::cout << (success ? "start ok" : "start failed") << std::endl;
return success ? 0 : 1;
}
if (command == "--stop") {
bool success = crossdesk::StopCrossDeskService();
std::cout << (success ? "stop ok" : "stop failed") << std::endl;
return success ? 0 : 1;
}
if (command == "--sas") {
std::cout << crossdesk::QueryCrossDeskService("sas") << std::endl;
return 0;
}
if (command == "--ping") {
std::cout << crossdesk::QueryCrossDeskService("ping") << std::endl;
return 0;
}
if (command == "--status") {
std::cout << crossdesk::QueryCrossDeskService("status") << std::endl;
return 0;
}
PrintUsage();
return 1;
}
File diff suppressed because it is too large Load Diff
+141
View File
@@ -0,0 +1,141 @@
#ifndef _CROSSDESK_SERVICE_HOST_H_
#define _CROSSDESK_SERVICE_HOST_H_
#include <Windows.h>
#include <mutex>
#include <string>
#include <thread>
namespace crossdesk {
inline constexpr wchar_t kCrossDeskServiceName[] = L"CrossDeskService";
inline constexpr wchar_t kCrossDeskServiceDisplayName[] =
L"CrossDesk Service";
inline constexpr wchar_t kCrossDeskServicePipeName[] =
L"\\\\.\\pipe\\CrossDeskService";
class CrossDeskServiceHost {
public:
CrossDeskServiceHost();
~CrossDeskServiceHost();
int RunAsService();
int RunInConsole();
private:
int RunServiceLoop(bool as_service);
int InitializeRuntime();
void ShutdownRuntime();
void RequestStop();
void ReportServiceStatus(DWORD current_state, DWORD win32_exit_code,
DWORD wait_hint);
void IpcServerLoop();
void RefreshSessionState();
void EnsureSessionHelper();
void ReapSessionHelper();
void StopSessionHelper();
bool LaunchSessionHelper(DWORD session_id);
void ReapSecureInputHelper();
void StopSecureInputHelper();
bool LaunchSecureInputHelper(DWORD session_id);
std::wstring GetSessionHelperPath() const;
std::wstring GetSessionHelperStopEventName(DWORD session_id) const;
std::wstring GetSecureInputHelperPath() const;
std::wstring GetSecureInputHelperStopEventName(DWORD session_id) const;
void ResetSessionHelperReportedStateLocked(const char* error,
DWORD error_code);
bool GetEffectiveSessionLockedLocked() const;
bool IsHelperReportingLockScreenLocked() const;
bool HasSecureInputUiLocked() const;
bool ShouldKeepSecureInputHelperLocked(DWORD target_session_id) const;
void RefreshSessionHelperReportedState();
void RecordSessionEvent(DWORD event_type, DWORD session_id);
std::string HandleIpcCommand(const std::string& command);
std::string BuildStatusResponse();
std::string SendSecureAttentionSequence();
std::string SendSecureDesktopKeyboardInput(int key_code, bool is_down);
static void WINAPI ServiceMain(DWORD argc, LPWSTR* argv);
static BOOL WINAPI ConsoleControlHandler(DWORD control_type);
static DWORD WINAPI ServiceControlHandler(DWORD control, DWORD event_type,
LPVOID event_data,
LPVOID context);
private:
SERVICE_STATUS_HANDLE status_handle_ = nullptr;
SERVICE_STATUS service_status_{};
HANDLE stop_event_ = nullptr;
std::thread ipc_thread_;
std::mutex state_mutex_;
DWORD active_session_id_ = 0xFFFFFFFF;
DWORD process_session_id_ = 0xFFFFFFFF;
DWORD input_desktop_error_code_ = 0;
DWORD session_helper_process_id_ = 0;
DWORD session_helper_session_id_ = 0xFFFFFFFF;
DWORD session_helper_exit_code_ = 0;
DWORD session_helper_last_error_code_ = 0;
DWORD session_helper_status_error_code_ = 0;
DWORD session_helper_report_session_id_ = 0xFFFFFFFF;
DWORD session_helper_report_process_id_ = 0;
DWORD session_helper_report_input_desktop_error_code_ = 0;
DWORD secure_input_helper_process_id_ = 0;
DWORD secure_input_helper_session_id_ = 0xFFFFFFFF;
DWORD secure_input_helper_exit_code_ = 0;
DWORD secure_input_helper_last_error_code_ = 0;
DWORD last_session_event_type_ = 0;
DWORD last_session_event_session_id_ = 0xFFFFFFFF;
ULONGLONG started_at_tick_ = 0;
ULONGLONG last_sas_tick_ = 0;
ULONGLONG session_helper_started_at_tick_ = 0;
ULONGLONG session_helper_report_state_age_ms_ = 0;
ULONGLONG session_helper_report_uptime_ms_ = 0;
ULONGLONG secure_input_helper_started_at_tick_ = 0;
bool session_locked_ = false;
bool logon_ui_visible_ = false;
bool prelogin_ = false;
bool secure_desktop_active_ = false;
bool input_desktop_available_ = false;
bool session_helper_running_ = false;
bool session_helper_status_ok_ = false;
bool session_helper_report_session_locked_ = false;
bool session_helper_report_input_desktop_available_ = false;
bool session_helper_report_lock_app_visible_ = false;
bool session_helper_report_logon_ui_visible_ = false;
bool session_helper_report_secure_desktop_active_ = false;
bool session_helper_report_credential_ui_visible_ = false;
bool session_helper_report_unlock_ui_visible_ = false;
bool secure_input_helper_running_ = false;
bool console_mode_ = false;
DWORD last_sas_error_code_ = 0;
bool last_sas_success_ = false;
HANDLE session_helper_process_handle_ = nullptr;
HANDLE session_helper_stop_event_ = nullptr;
HANDLE secure_input_helper_process_handle_ = nullptr;
HANDLE secure_input_helper_stop_event_ = nullptr;
std::string input_desktop_name_;
std::string last_sas_error_;
std::string session_helper_last_error_;
std::string session_helper_status_error_;
std::string session_helper_report_input_desktop_;
std::string session_helper_report_interactive_stage_;
std::string secure_input_helper_last_error_;
static CrossDeskServiceHost* instance_;
};
bool InstallCrossDeskService(const std::wstring& binary_path);
bool UninstallCrossDeskService();
bool StartCrossDeskService();
bool StopCrossDeskService(DWORD timeout_ms = 5000);
std::string QueryCrossDeskService(const std::string& command,
DWORD timeout_ms = 1000);
std::string SendCrossDeskSecureDesktopKeyInput(int key_code, bool is_down,
DWORD timeout_ms = 1000);
std::string SendCrossDeskSecureDesktopMouseInput(int x, int y, int wheel,
int flag,
DWORD timeout_ms = 1000);
} // namespace crossdesk
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,51 @@
#ifndef _CROSSDESK_SESSION_HELPER_SHARED_H_
#define _CROSSDESK_SESSION_HELPER_SHARED_H_
#include <Windows.h>
#include <cstdint>
#include <string>
namespace crossdesk {
inline constexpr wchar_t kCrossDeskSessionHelperPipePrefix[] =
L"\\\\.\\pipe\\CrossDeskSessionHelper-";
inline constexpr wchar_t kCrossDeskSecureInputHelperPipePrefix[] =
L"\\\\.\\pipe\\CrossDeskSecureInputHelper-";
inline constexpr char kCrossDeskSessionHelperStatusCommand[] = "status";
inline constexpr char kCrossDeskSecureInputKeyboardCommandPrefix[] =
"keyboard:";
inline constexpr char kCrossDeskSecureInputMouseCommandPrefix[] =
"mouse:";
inline constexpr char kCrossDeskSecureInputCaptureCommandPrefix[] =
"capture:";
inline constexpr DWORD kCrossDeskSecureInputPipeBufferBytes =
16 * 1024 * 1024;
inline constexpr uint32_t kCrossDeskSecureDesktopFrameMagic = 0x50444358;
inline constexpr uint32_t kCrossDeskSecureDesktopFrameVersion = 1;
#pragma pack(push, 1)
struct CrossDeskSecureDesktopFrameHeader {
uint32_t magic;
uint32_t version;
int32_t left;
int32_t top;
uint32_t width;
uint32_t height;
uint32_t payload_size;
};
#pragma pack(pop)
inline std::wstring GetCrossDeskSessionHelperPipeName(DWORD session_id) {
return std::wstring(kCrossDeskSessionHelperPipePrefix) +
std::to_wstring(session_id);
}
inline std::wstring GetCrossDeskSecureInputHelperPipeName(DWORD session_id) {
return std::wstring(kCrossDeskSecureInputHelperPipePrefix) +
std::to_wstring(session_id);
}
} // namespace crossdesk
#endif