diff --git a/Cargo.lock b/Cargo.lock index d8ccaf5fc..d28cfc15a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5666,7 +5666,7 @@ version = "0.1.0" dependencies = [ "hbb_common", "winapi 0.3.9", - "windows-strings", + "windows-strings 0.3.1", ] [[package]] @@ -5947,6 +5947,7 @@ dependencies = [ "wallpaper", "whoami", "winapi 0.3.9", + "windows 0.61.1", "windows-service", "winreg 0.11.0", "winres", @@ -6734,7 +6735,7 @@ dependencies = [ "unicode-segmentation", "url", "windows 0.52.0", - "windows-implement", + "windows-implement 0.52.0", "windows-version", "x11-dl", "zbus", @@ -7923,8 +7924,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core 0.52.0", - "windows-implement", - "windows-interface", + "windows-implement 0.52.0", + "windows-interface 0.52.0", "windows-targets 0.52.5", ] @@ -7938,6 +7939,28 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +dependencies = [ + "windows-collections", + "windows-core 0.61.0", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.0", +] + [[package]] name = "windows-core" version = "0.51.1" @@ -7962,10 +7985,33 @@ version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" dependencies = [ - "windows-result", + "windows-result 0.1.2", "windows-targets 0.52.5", ] +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.2", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +dependencies = [ + "windows-core 0.61.0", + "windows-link", +] + [[package]] name = "windows-implement" version = "0.52.0" @@ -7977,6 +8023,17 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.36", + "syn 2.0.98", +] + [[package]] name = "windows-interface" version = "0.52.0" @@ -7988,12 +8045,33 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2 1.0.93", + "quote 1.0.36", + "syn 2.0.98", +] + [[package]] name = "windows-link" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.0", + "windows-link", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -8003,6 +8081,15 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-service" version = "0.6.0" @@ -8023,6 +8110,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index 078854bcd..2fb22dc29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,6 +118,13 @@ winapi = { version = "0.3", features = [ "ioapiset", "winspool", ] } +windows = { version = "0.61.1", features = [ + "Win32", + "Win32_System", + "Win32_System_Diagnostics", + "Win32_System_Threading", + "Win32_System_Diagnostics_ToolHelp", +] } winreg = "0.11" windows-service = "0.6" virtual_display = { path = "libs/virtual_display" } diff --git a/libs/hbb_common b/libs/hbb_common index 1ed5a469c..ebb4d4a48 160000 --- a/libs/hbb_common +++ b/libs/hbb_common @@ -1 +1 @@ -Subproject commit 1ed5a469cfa2318b4045859cab6c88889717193a +Subproject commit ebb4d4a48cf7ed6ca62e93f8ed124065c6408536 diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 08cf0fb9a..5c1a1cf2c 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -623,6 +623,31 @@ pub fn is_prelogin() -> bool { n < 4 && n > 1 } +// Check "Lock". +// "Switch user" can't be checked, because `get_values_of_seat0(&[0])` does not return the session. +// The logged in session is "online" not "active". +// And the "Switch user" screen is usually Wayland login session, which we do not support. +pub fn is_locked() -> bool { + if is_prelogin() { + return false; + } + + let values = get_values_of_seat0(&[0]); + // Though the values can't be empty, we still add check here for safety. + // Because we cannot guarantee whether the internal implementation will change in the future. + // https://github.com/rustdesk/hbb_common/blob/ebb4d4a48cf7ed6ca62e93f8ed124065c6408536/src/platform/linux.rs#L119 + if values.is_empty() { + log::debug!("Failed to check is locked, values vector is empty."); + return false; + } + let session = &values[0]; + if session.is_empty() { + log::debug!("Failed to check is locked, session is empty."); + return false; + } + is_session_locked(session) +} + pub fn is_root() -> bool { crate::username() == "root" } diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 2fb2b46db..853e7edfd 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -491,6 +491,38 @@ pub fn is_prelogin() -> bool { get_active_userid() == "0" } +// https://stackoverflow.com/questions/11505255/osx-check-if-the-screen-is-locked +// No "CGSSessionScreenIsLocked" can be found when macOS is not locked. +// +// `ioreg -n Root -d1` returns `"CGSSessionScreenIsLocked"=Yes` +// `ioreg -n Root -d1 -a` returns +// ``` +// ... +// CGSSessionScreenIsLocked +// +// ... +// ``` +pub fn is_locked() -> bool { + match std::process::Command::new("ioreg") + .arg("-n") + .arg("Root") + .arg("-d1") + .output() + { + Ok(output) => { + let output_str = String::from_utf8_lossy(&output.stdout); + // Although `"CGSSessionScreenIsLocked"=Yes` was printed on my macOS, + // I also check `"CGSSessionScreenIsLocked"=true` for better compability. + output_str.contains("\"CGSSessionScreenIsLocked\"=Yes") + || output_str.contains("\"CGSSessionScreenIsLocked\"=true") + } + Err(e) => { + log::error!("Failed to query ioreg for the lock state: {}", e); + false + } + } +} + pub fn is_root() -> bool { crate::username() == "root" } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 015b6a332..087ca485d 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -21,7 +21,10 @@ use std::{ fs, io::{self, prelude::*}, mem, - os::{raw::c_ulong, windows::process::CommandExt}, + os::{ + raw::c_ulong, + windows::{ffi::OsStringExt, process::CommandExt}, + }, path::*, ptr::null_mut, sync::{atomic::Ordering, Arc, Mutex}, @@ -60,6 +63,13 @@ use winapi::{ winuser::*, }, }; +use windows::Win32::{ + Foundation::{CloseHandle as WinCloseHandle, HANDLE as WinHANDLE}, + System::Diagnostics::ToolHelp::{ + CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, + TH32CS_SNAPPROCESS, + }, +}; use windows_service::{ define_windows_service, service::{ @@ -956,6 +966,19 @@ pub fn is_prelogin() -> bool { username.is_empty() || username == "SYSTEM" } +// `is_logon_ui()` is regardless of multiple sessions now. +// It only check if "LogonUI.exe" exists. +// +// If there're mulitple sessions (logged in users), +// some are in the login screen, while the others are not. +// Then this function may not work fine if the session we want to handle(connect) is not in the login screen. +// But it's a rare case and cannot be simply handled, so it will not be dealt with for the time being. +#[inline] +pub fn is_logon_ui() -> ResultType { + let pids = get_pids("LogonUI.exe")?; + Ok(!pids.is_empty()) +} + pub fn is_root() -> bool { // https://stackoverflow.com/questions/4023586/correct-way-to-find-out-if-a-service-is-running-as-the-system-user unsafe { is_local_system() == TRUE } @@ -2916,3 +2939,38 @@ pub fn send_raw_data_to_printer(printer_name: Option, data: Vec) -> Ok(()) } + +fn get_pids>(name: S) -> ResultType> { + let name = name.as_ref().to_lowercase(); + let mut pids = Vec::new(); + + unsafe { + let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)?; + if snapshot == WinHANDLE::default() { + return Ok(pids); + } + + let mut entry: PROCESSENTRY32W = std::mem::zeroed(); + entry.dwSize = std::mem::size_of::() as u32; + + if Process32FirstW(snapshot, &mut entry).is_ok() { + loop { + let proc_name = OsString::from_wide(&entry.szExeFile) + .to_string_lossy() + .to_lowercase(); + + if proc_name.contains(&name) { + pids.push(entry.th32ProcessID); + } + + if !Process32NextW(snapshot, &mut entry).is_ok() { + break; + } + } + } + + let _ = WinCloseHandle(snapshot); + } + + Ok(pids) +} diff --git a/src/server/connection.rs b/src/server/connection.rs index 24038effd..46e19315d 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1946,6 +1946,27 @@ impl Connection { return true; } + // https://github.com/rustdesk/rustdesk-server-pro/discussions/646 + // `is_logon` is used to check login with `OPTION_ALLOW_LOGON_SCREEN_PASSWORD` == "Y". + // `is_logon_ui()` is used on Windows, because there's no good way to detect `is_locked()`. + // Detecting `is_logon_ui()` (if `LogonUI.exe` running) is a workaround. + #[cfg(target_os = "windows")] + let is_logon = || { + crate::platform::is_prelogin() || { + match crate::platform::is_logon_ui() { + Ok(result) => result, + Err(e) => { + log::error!("Failed to detect logon UI: {:?}", e); + false + } + } + } + }; + #[cfg(any(target_os = "linux", target_os = "macos"))] + let is_logon = || crate::platform::is_prelogin() || crate::platform::is_locked(); + #[cfg(any(target_os = "android", target_os = "ios"))] + let is_logon = || crate::platform::is_prelogin(); + if !hbb_common::is_ip_str(&lr.username) && !hbb_common::is_domain_port_str(&lr.username) && lr.username != Config::get_id() @@ -1954,8 +1975,8 @@ impl Connection { .await; return false; } else if (password::approve_mode() == ApproveMode::Click - && !(crate::platform::is_prelogin() - && crate::get_builtin_option(keys::OPTION_ALLOW_LOGON_SCREEN_PASSWORD) == "Y")) + && !(crate::get_builtin_option(keys::OPTION_ALLOW_LOGON_SCREEN_PASSWORD) == "Y" + && is_logon())) || password::approve_mode() == ApproveMode::Both && !password::has_valid_password() { self.try_start_cm(lr.my_id, lr.my_name, false);