fix: linux, home (#13879)

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2025-12-23 15:43:31 +08:00
committed by GitHub
parent eba847e62e
commit 6a701f1420
5 changed files with 61 additions and 21 deletions

2
Cargo.lock generated
View File

@@ -3749,6 +3749,7 @@ dependencies = [
"toml 0.7.8",
"tungstenite",
"url",
"users 0.11.0",
"uuid",
"webpki-roots 1.0.4",
"webrtc",
@@ -7231,7 +7232,6 @@ dependencies = [
"tray-icon",
"ttf-parser",
"url",
"users 0.11.0",
"uuid",
"virtual_display",
"wallpaper",

View File

@@ -176,7 +176,6 @@ evdev = { git="https://github.com/rustdesk-org/evdev" }
dbus = "0.9"
dbus-crossroads = "0.5"
pam = { git="https://github.com/rustdesk-org/pam" }
users = { version = "0.11" }
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch", optional = true}
x11rb = {version = "0.12", features = ["all-extensions"], optional = true}
percent-encoding = {version = "2.3", optional = true}

View File

@@ -1,21 +1,20 @@
use super::{gtk_sudo, CursorData, ResultType};
use desktop::Desktop;
use hbb_common::config::keys::OPTION_ALLOW_LINUX_HEADLESS;
pub use hbb_common::platform::linux::*;
use hbb_common::{
allow_err,
anyhow::anyhow,
bail,
config::Config,
config::{keys::OPTION_ALLOW_LINUX_HEADLESS, Config},
libc::{c_char, c_int, c_long, c_void},
log,
message_proto::{DisplayInfo, Resolution},
regex::{Captures, Regex},
users::{get_user_by_name, os::unix::UserExt},
};
use std::{
cell::RefCell,
ffi::{OsStr, OsString},
os::unix::ffi::OsStrExt,
path::{Path, PathBuf},
process::{Child, Command},
string::String,
@@ -26,7 +25,6 @@ use std::{
time::{Duration, Instant},
};
use terminfo::{capability as cap, Database};
use users::{get_user_by_name, os::unix::UserExt};
use wallpaper;
type Xdo = *const c_void;
@@ -1714,26 +1712,57 @@ pub fn run_cmds_privileged(cmds: &str) -> bool {
crate::platform::gtk_sudo::run(vec![cmds]).is_ok()
}
/// Spawn the current executable after a delay.
///
/// # Security
/// The executable path is safely quoted using `shell_quote()` to prevent
/// command injection vulnerabilities. The `secs` parameter is a u32, so it
/// cannot contain malicious input.
///
/// # Arguments
/// * `secs` - Number of seconds to wait before spawning
pub fn run_me_with(secs: u32) {
let exe = std::env::current_exe()
.unwrap_or("".into())
.to_string_lossy()
.to_string();
// We use `CMD_SH` instead of `sh` to suppress some audit messages on some systems.
std::process::Command::new(CMD_SH.as_str())
let exe = match std::env::current_exe() {
Ok(path) => path,
Err(e) => {
log::error!("Failed to get current exe: {}", e);
return;
}
};
// SECURITY: Use shell_quote to safely escape the executable path,
// preventing command injection even if the path contains special characters.
let exe_quoted = shell_quote(&exe.to_string_lossy());
// Spawn a background process that sleeps and then executes.
// The child process is automatically orphaned when parent exits,
// and will be adopted by init (PID 1).
Command::new(CMD_SH.as_str())
.arg("-c")
.arg(&format!("sleep {secs}; {exe}"))
.arg(&format!("sleep {secs}; exec {exe_quoted}"))
.spawn()
.ok();
}
fn switch_service(stop: bool) -> String {
let home = std::env::var("HOME").unwrap_or_default();
// SECURITY: Use trusted home directory lookup via getpwuid instead of $HOME env var
// to prevent confused-deputy attacks where an attacker manipulates environment variables.
let home = get_home_dir_trusted()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_default();
Config::set_option("stop-service".into(), if stop { "Y" } else { "" }.into());
if home != "/root" && !Config::get().is_empty() {
let p = format!(".config/{}", crate::get_app_name().to_lowercase());
if !home.is_empty() && home != "/root" && !Config::get().is_empty() {
let app_name_lower = crate::get_app_name().to_lowercase();
let app_name0 = crate::get_app_name();
format!("cp -f {home}/{p}/{app_name0}.toml /root/{p}/; cp -f {home}/{p}/{app_name0}2.toml /root/{p}/;")
let config_subdir = format!(".config/{}", app_name_lower);
// SECURITY: Quote all paths to prevent shell injection from paths containing
// spaces, semicolons, or other special characters.
let src1 = shell_quote(&format!("{}/{}/{}.toml", home, config_subdir, app_name0));
let src2 = shell_quote(&format!("{}/{}/{}2.toml", home, config_subdir, app_name0));
let dst = shell_quote(&format!("/root/{}/", config_subdir));
format!("cp -f {} {}; cp -f {} {};", src1, dst, src2, dst)
} else {
"".to_owned()
}
@@ -1787,7 +1816,15 @@ fn check_if_stop_service() {
}
pub fn check_autostart_config() -> ResultType<()> {
let home = std::env::var("HOME").unwrap_or_default();
// SECURITY: Use trusted home directory lookup via getpwuid instead of $HOME env var
// to prevent confused-deputy attacks where an attacker manipulates environment variables.
let home = match get_home_dir_trusted() {
Some(p) => p.to_string_lossy().to_string(),
None => {
log::warn!("Failed to get trusted home directory for autostart config check");
return Ok(());
}
};
let app_name = crate::get_app_name().to_lowercase();
let path = format!("{home}/.config/autostart");
let file = format!("{path}/{app_name}.desktop");

View File

@@ -4,7 +4,12 @@ use crate::client::{
LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LOGIN_MSG_DESKTOP_XORG_NOT_FOUND,
LOGIN_MSG_DESKTOP_XSESSION_FAILED,
};
use hbb_common::{allow_err, bail, log, rand::prelude::*, tokio::time};
use hbb_common::{
allow_err, bail, log,
rand::prelude::*,
tokio::time,
users::{get_user_by_name, os::unix::UserExt, User},
};
use pam;
use std::{
collections::HashMap,
@@ -18,7 +23,6 @@ use std::{
},
time::{Duration, Instant},
};
use users::{get_user_by_name, os::unix::UserExt, User};
lazy_static::lazy_static! {
static ref DESKTOP_RUNNING: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));