fix(ipc): harden local IPC authorization and portable-service bootstrap flow (#14671)

* fix(ipc): harden ipc access

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): full cmd path, comments, simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): portable service, ipc exit

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): Remove unused logs

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): Use SetEntriesInAclW instead of icacls

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): Comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): check is_reparse_point

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): shmem name, no fallback

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): Simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): better exit and clear

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): portable service, better exit

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): comments, id -u

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: comments linux headless, rx desktop ready

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): magic number

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): update deps

Signed-off-by: fufesou <linlong1266@gmail.com>

* Update Cargo.lock

* Update Cargo.lock

* fix(ipc): harden ipc, test `identity_unavailable`

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): portable service, check dir of shmem

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): macos, better check exe allowed

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): update hbb_common

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): update hbb_common

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): harden ipc, better active uid for uinput

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): harden portable service token validation

Compare portable service IPC tokens in constant time and document the
CSPRNG source used for one-time token generation. Clarify Windows IPC
authorization comments around canonical path matching and partial peer
identity lookup.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): harden portable service token handling

Generate the portable service IPC token directly from OsRng, keep token
comparison in the IPC layer as a fixed-length byte-wise check, and document
the malformed-frame behavior for protected service IPC.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ipc): comments

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
fufesou
2026-05-09 18:15:00 +08:00
committed by GitHub
parent 72d27c3c47
commit 9df486a689
12 changed files with 4500 additions and 249 deletions

View File

@@ -29,6 +29,12 @@ use wallpaper;
pub const PA_SAMPLE_RATE: u32 = 48000;
static mut UNMODIFIED: bool = true;
#[derive(Clone, Debug)]
struct ActiveUserLookupCache {
uid: String,
username: String,
}
const INVALID_TERM_VALUES: [&str; 3] = ["", "unknown", "dumb"];
const SHELL_PROCESSES: [&str; 4] = ["bash", "zsh", "fish", "sh"];
@@ -50,6 +56,8 @@ lazy_static::lazy_static! {
}
}
};
static ref ACTIVE_USER_LOOKUP_CACHE: std::sync::Mutex<Option<ActiveUserLookupCache>> =
std::sync::Mutex::new(None);
// https://github.com/rustdesk/rustdesk/issues/13705
// Check if `sudo -E` actually preserves environment.
//
@@ -82,6 +90,27 @@ lazy_static::lazy_static! {
};
}
#[inline]
fn update_active_user_lookup_cache(desktop: &Desktop) {
if let Ok(mut cache) = ACTIVE_USER_LOOKUP_CACHE.lock() {
if desktop.uid.is_empty() || desktop.username.is_empty() {
*cache = None;
} else {
*cache = Some(ActiveUserLookupCache {
uid: desktop.uid.clone(),
username: desktop.username.clone(),
});
}
}
}
#[inline]
fn get_active_user_id_name_from_cache() -> Option<(String, String)> {
let cache = ACTIVE_USER_LOOKUP_CACHE.lock().ok()?;
let entry = cache.as_ref()?;
Some((entry.uid.clone(), entry.username.clone()))
}
thread_local! {
// XDO context - created via libxdo-sys (which uses dynamic loading stub).
// If libxdo is not available, xdo will be null and xdo-based functions become no-ops.
@@ -789,6 +818,7 @@ pub fn start_os_service() {
let mut last_restart = Instant::now();
while running.load(Ordering::SeqCst) {
desktop.refresh();
update_active_user_lookup_cache(&desktop);
// Duplicate logic here with should_start_server
// Login wayland will try to start a headless --server.
@@ -861,13 +891,29 @@ pub fn start_os_service() {
}
#[inline]
/// Returns the cached active `(uid, username)` snapshot when available.
/// Callers that require a fresh seat0 lookup should call `get_values_of_seat0` directly.
pub fn get_active_user_id_name() -> (String, String) {
if let Some(id_name) = get_active_user_id_name_from_cache() {
return id_name;
}
let vec_id_name = get_values_of_seat0(&[1, 2]);
(vec_id_name[0].clone(), vec_id_name[1].clone())
}
#[inline]
/// Returns the cached active uid when available.
/// Callers that require a fresh seat0 lookup should call `get_values_of_seat0` directly.
pub fn get_active_userid() -> String {
if let Some((uid, _)) = get_active_user_id_name_from_cache() {
return uid;
}
get_values_of_seat0(&[1])[0].clone()
}
#[inline]
/// Returns the active uid from a fresh seat0 lookup, bypassing the service-loop cache.
pub fn get_active_userid_fresh() -> String {
get_values_of_seat0(&[1])[0].clone()
}
@@ -922,7 +968,12 @@ fn _get_display_manager() -> String {
}
#[inline]
/// Returns the cached active username when available.
/// Callers that require a fresh seat0 lookup should call `get_values_of_seat0` directly.
pub fn get_active_username() -> String {
if let Some((_, username)) = get_active_user_id_name_from_cache() {
return username;
}
get_values_of_seat0(&[2])[0].clone()
}