mirror of
https://github.com/rustdesk/hbb_common.git
synced 2026-02-16 02:20:43 +00:00
feat: linux, get_home_trusted
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -95,3 +95,4 @@ osascript = "0.3"
|
|||||||
sctk = { package = "smithay-client-toolkit", version = "0.20.0", default-features = false, features = [
|
sctk = { package = "smithay-client-toolkit", version = "0.20.0", default-features = false, features = [
|
||||||
"calloop",
|
"calloop",
|
||||||
] }
|
] }
|
||||||
|
users = { version = "0.11" }
|
||||||
|
|||||||
@@ -626,6 +626,23 @@ impl Config {
|
|||||||
(self.id.is_empty() && self.enc_id.is_empty()) || self.key_pair.0.is_empty()
|
(self.id.is_empty() && self.enc_id.is_empty()) || self.key_pair.0.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the user's home directory for configuration purposes.
|
||||||
|
///
|
||||||
|
/// # Security Note
|
||||||
|
/// This function uses `dirs_next::home_dir()` which reads the `$HOME` environment
|
||||||
|
/// variable on Unix systems. This is acceptable for user-space operations (config
|
||||||
|
/// file storage, logging) where the user may intentionally redirect their home
|
||||||
|
/// directory.
|
||||||
|
///
|
||||||
|
/// **DO NOT use this function in privileged contexts** (e.g., code executed via
|
||||||
|
/// `gtk_sudo` or system services running as root). For privileged operations on
|
||||||
|
/// Linux, use `crate::platform::linux::get_home_dir_trusted()` which bypasses
|
||||||
|
/// the `$HOME` environment variable and queries the system password database
|
||||||
|
/// directly via `getpwuid`.
|
||||||
|
///
|
||||||
|
/// Using `$HOME` in privileged contexts creates a confused-deputy vulnerability
|
||||||
|
/// where an attacker can manipulate the environment variable to inject malicious
|
||||||
|
/// paths into privileged operations.
|
||||||
pub fn get_home() -> PathBuf {
|
pub fn get_home() -> PathBuf {
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
return PathBuf::from(APP_HOME_DIR.read().unwrap().as_str());
|
return PathBuf::from(APP_HOME_DIR.read().unwrap().as_str());
|
||||||
@@ -666,6 +683,12 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the log directory path.
|
||||||
|
///
|
||||||
|
/// # Security Note
|
||||||
|
/// On macOS, this function uses `dirs_next::home_dir()` which reads the `$HOME`
|
||||||
|
/// environment variable. On Linux/Android, it uses `Self::get_home()`.
|
||||||
|
/// See [`Self::get_home()`] for security considerations regarding `$HOME` usage.
|
||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
pub fn log_path() -> PathBuf {
|
pub fn log_path() -> PathBuf {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ pub use whoami;
|
|||||||
pub mod tls;
|
pub mod tls;
|
||||||
pub mod verifier;
|
pub mod verifier;
|
||||||
pub use async_recursion;
|
pub use async_recursion;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub use users;
|
||||||
|
|
||||||
pub type SessionID = uuid::Uuid;
|
pub type SessionID = uuid::Uuid;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
use crate::ResultType;
|
use crate::ResultType;
|
||||||
use std::{collections::HashMap, process::Command};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
use users::{get_current_uid, get_user_by_uid, os::unix::UserExt};
|
||||||
|
|
||||||
use sctk::{
|
use sctk::{
|
||||||
output::OutputData,
|
output::OutputData,
|
||||||
@@ -442,6 +447,56 @@ pub fn get_wayland_displays() -> ResultType<Vec<WaylandDisplayInfo>> {
|
|||||||
Ok(display_infos)
|
Ok(display_infos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Escape a string for safe use in shell commands by wrapping in single quotes.
|
||||||
|
///
|
||||||
|
/// This function handles the edge case of single quotes within the string by:
|
||||||
|
/// 1. Ending the current single-quoted section
|
||||||
|
/// 2. Adding an escaped single quote
|
||||||
|
/// 3. Starting a new single-quoted section
|
||||||
|
///
|
||||||
|
/// Example: "it's here" -> "'it'\''s here'"
|
||||||
|
#[inline]
|
||||||
|
pub fn shell_quote(s: &str) -> String {
|
||||||
|
format!("'{}'", s.replace("'", "'\\''"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current user's home directory via getpwuid (trusted source).
|
||||||
|
///
|
||||||
|
/// This function uses the system's password database (via `getpwuid`) to retrieve
|
||||||
|
/// the home directory, avoiding the security risk of relying on the `HOME`
|
||||||
|
/// environment variable which can be manipulated by untrusted input.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Some(PathBuf)` if the home directory was found and exists
|
||||||
|
/// - `None` if the user lookup failed or the directory doesn't exist
|
||||||
|
///
|
||||||
|
/// # Security
|
||||||
|
/// This function is designed to be safe against confused-deputy attacks where
|
||||||
|
/// an attacker might manipulate environment variables to influence privileged
|
||||||
|
/// operations.
|
||||||
|
pub fn get_home_dir_trusted() -> Option<PathBuf> {
|
||||||
|
let uid = get_current_uid();
|
||||||
|
match get_user_by_uid(uid) {
|
||||||
|
Some(user) => {
|
||||||
|
let home = user.home_dir();
|
||||||
|
if Path::is_dir(home) {
|
||||||
|
Some(PathBuf::from(home))
|
||||||
|
} else {
|
||||||
|
log::warn!(
|
||||||
|
"Home directory for uid {} does not exist or is not a directory: {:?}",
|
||||||
|
uid,
|
||||||
|
home
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
log::warn!("Failed to get user info for uid {}", uid);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -455,4 +510,63 @@ mod tests {
|
|||||||
run_cmds("whoami").unwrap()
|
run_cmds("whoami").unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test get_home_dir_trusted: returns valid path and ignores HOME env var
|
||||||
|
#[test]
|
||||||
|
fn test_get_home_dir_trusted() {
|
||||||
|
let original_home = std::env::var("HOME").ok();
|
||||||
|
|
||||||
|
// Set HOME to a fake/malicious path
|
||||||
|
std::env::set_var("HOME", "/tmp/fake_malicious_home");
|
||||||
|
let result = get_home_dir_trusted();
|
||||||
|
|
||||||
|
// Restore original HOME
|
||||||
|
match original_home {
|
||||||
|
Some(home) => std::env::set_var("HOME", home),
|
||||||
|
None => std::env::remove_var("HOME"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify: returns valid path that is NOT the fake HOME
|
||||||
|
if let Some(path) = result {
|
||||||
|
assert!(path.is_absolute(), "Path should be absolute: {:?}", path);
|
||||||
|
assert!(path.is_dir(), "Path should be a directory: {:?}", path);
|
||||||
|
assert_ne!(
|
||||||
|
path.to_string_lossy(),
|
||||||
|
"/tmp/fake_malicious_home",
|
||||||
|
"Should not use HOME env var"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test shell_quote with normal strings
|
||||||
|
#[test]
|
||||||
|
fn test_shell_quote_normal() {
|
||||||
|
assert_eq!(shell_quote("hello"), "'hello'");
|
||||||
|
assert_eq!(shell_quote("/home/user"), "'/home/user'");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test shell_quote with spaces
|
||||||
|
#[test]
|
||||||
|
fn test_shell_quote_spaces() {
|
||||||
|
assert_eq!(shell_quote("/home/my user/file"), "'/home/my user/file'");
|
||||||
|
assert_eq!(shell_quote("path with spaces"), "'path with spaces'");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test shell_quote with single quotes (the tricky case)
|
||||||
|
#[test]
|
||||||
|
fn test_shell_quote_single_quotes() {
|
||||||
|
assert_eq!(shell_quote("it's"), "'it'\\''s'");
|
||||||
|
assert_eq!(shell_quote("don't stop"), "'don'\\''t stop'");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test shell_quote with shell metacharacters
|
||||||
|
#[test]
|
||||||
|
fn test_shell_quote_metacharacters() {
|
||||||
|
// These should all be safely quoted
|
||||||
|
assert_eq!(shell_quote("test;rm -rf /"), "'test;rm -rf /'");
|
||||||
|
assert_eq!(shell_quote("$(whoami)"), "'$(whoami)'");
|
||||||
|
assert_eq!(shell_quote("`id`"), "'`id`'");
|
||||||
|
assert_eq!(shell_quote("a && b"), "'a && b'");
|
||||||
|
assert_eq!(shell_quote("a | b"), "'a | b'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user