diff --git a/Cargo.toml b/Cargo.toml index a366691..d21c864 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ thiserror = "2" # Unix/Linux nix = { version = "0.30", features = ["user", "fs", "process", "signal", "term", "resource"] } +rustix = { version = "1", features = ["process", "fs"] } libc = "0.2" # Security diff --git a/src/shadow-core/Cargo.toml b/src/shadow-core/Cargo.toml index b602c0c..690fdcb 100644 --- a/src/shadow-core/Cargo.toml +++ b/src/shadow-core/Cargo.toml @@ -17,6 +17,7 @@ path = "src/lib.rs" [dependencies] libc = { workspace = true } nix = { workspace = true } +rustix = { workspace = true } thiserror = { workspace = true } zeroize = { workspace = true } subtle = { workspace = true } diff --git a/src/shadow-core/src/atomic.rs b/src/shadow-core/src/atomic.rs index ecb6cec..888eb56 100644 --- a/src/shadow-core/src/atomic.rs +++ b/src/shadow-core/src/atomic.rs @@ -30,16 +30,13 @@ use crate::error::ShadowError; /// `umask(2)` is a process-wide operation. This guard is NOT safe to use /// from multiple threads concurrently. All shadow-rs tools are /// single-threaded, so this is not an issue in practice. -struct UmaskGuard( - nix::sys::stat::Mode, - std::marker::PhantomData>, -); +struct UmaskGuard(rustix::fs::Mode, std::marker::PhantomData>); impl UmaskGuard { /// Set umask to zero and return a guard that restores the original. fn zero() -> Self { Self( - nix::sys::stat::umask(nix::sys::stat::Mode::empty()), + rustix::process::umask(rustix::fs::Mode::empty()), std::marker::PhantomData, ) } @@ -47,7 +44,7 @@ impl UmaskGuard { impl Drop for UmaskGuard { fn drop(&mut self) { - nix::sys::stat::umask(self.0); + rustix::process::umask(self.0); } } @@ -151,7 +148,7 @@ where tmp_file .flush() .map_err(|e| ShadowError::IoPath(e, tmp_path.clone()))?; - nix::unistd::fsync(&tmp_file) + rustix::fs::fsync(&tmp_file) .map_err(|e| ShadowError::IoPath(io::Error::from(e), tmp_path.clone()))?; // Atomic rename. @@ -162,7 +159,7 @@ where // Fsync the parent directory to ensure the rename is durable. if let Ok(dir_fd) = File::open(dir) { - let _ = nix::unistd::fsync(&dir_fd); + let _ = rustix::fs::fsync(&dir_fd); } Ok(()) diff --git a/src/shadow-core/src/lock.rs b/src/shadow-core/src/lock.rs index 0140d30..984ecd0 100644 --- a/src/shadow-core/src/lock.rs +++ b/src/shadow-core/src/lock.rs @@ -19,8 +19,6 @@ use std::path::{Path, PathBuf}; use std::thread; use std::time::{Duration, Instant}; -use nix::unistd; - use crate::error::ShadowError; /// Default lock timeout (matches GNU shadow-utils `LOCK_TIMEOUT`). @@ -187,7 +185,7 @@ fn write_pid_file(tmp_path: &Path) -> Result<(), ShadowError> { } }; - let pid = unistd::getpid(); + let pid = rustix::process::getpid(); write!(file, "{pid}").map_err(|e| { ShadowError::Lock(format!("cannot write {}: {e}", tmp_path.display()).into()) })?; @@ -195,7 +193,7 @@ fn write_pid_file(tmp_path: &Path) -> Result<(), ShadowError> { file.flush().map_err(|e| { ShadowError::Lock(format!("cannot flush {}: {e}", tmp_path.display()).into()) })?; - nix::unistd::fsync(&file).map_err(|e| { + rustix::fs::fsync(&file).map_err(|e| { ShadowError::Lock(format!("cannot fsync {}: {e}", tmp_path.display()).into()) })?; @@ -217,13 +215,16 @@ fn is_stale_lock(lock_path: &Path) -> bool { return true; } + let Some(pid) = rustix::process::Pid::from_raw(pid) else { + return true; + }; + // Signal 0 checks if the process exists without actually sending a signal. // Only ESRCH means "no such process". EPERM means the process exists but // we lack permission to signal it — that is a valid lock holder. - let pid = nix::unistd::Pid::from_raw(pid); matches!( - nix::sys::signal::kill(pid, None), - Err(nix::errno::Errno::ESRCH) + rustix::process::test_kill_process(pid), + Err(rustix::io::Errno::SRCH) ) } @@ -305,6 +306,8 @@ mod tests { let lock = FileLock::acquire(&file).expect("failed to acquire lock"); let f = fs::File::open(&lock.lock_path).expect("failed to open lock file"); + // Uses nix for this test since rustix lacks fcntl_getfd. The nix + // dependency remains in shadow-core for hardening.rs and pam.rs. let flags = nix::fcntl::fcntl(&f, nix::fcntl::FcntlArg::F_GETFD).expect("fcntl F_GETFD failed"); assert!(flags & libc::FD_CLOEXEC != 0, "FD should have CLOEXEC set"); diff --git a/src/shadow-core/src/skel.rs b/src/shadow-core/src/skel.rs index 7239032..0d01a92 100644 --- a/src/shadow-core/src/skel.rs +++ b/src/shadow-core/src/skel.rs @@ -91,7 +91,7 @@ mod tests { #[test] fn test_copy_files() { - if !nix::unistd::geteuid().is_root() { + if !rustix::process::geteuid().is_root() { return; } @@ -116,7 +116,7 @@ mod tests { #[test] fn test_copy_subdirectory() { - if !nix::unistd::geteuid().is_root() { + if !rustix::process::geteuid().is_root() { return; } @@ -135,7 +135,7 @@ mod tests { #[test] fn test_copy_symlink() { - if !nix::unistd::geteuid().is_root() { + if !rustix::process::geteuid().is_root() { return; } diff --git a/src/uu/chage/Cargo.toml b/src/uu/chage/Cargo.toml index 180169b..8b07ae1 100644 --- a/src/uu/chage/Cargo.toml +++ b/src/uu/chage/Cargo.toml @@ -21,6 +21,7 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } nix = { workspace = true } +rustix = { workspace = true } shadow-core = { workspace = true, features = ["shadow"] } uucore = { workspace = true } diff --git a/src/uu/chage/src/chage.rs b/src/uu/chage/src/chage.rs index 79f2322..0ddb722 100644 --- a/src/uu/chage/src/chage.rs +++ b/src/uu/chage/src/chage.rs @@ -518,10 +518,10 @@ fn do_chroot(dir: &str) -> Result<(), ChageError> { } let path = Path::new(dir); - nix::unistd::chroot(path) + rustix::process::chroot(path) .map_err(|e| ChageError::UnexpectedFailure(format!("cannot chroot to '{dir}': {e}")))?; - nix::unistd::chdir("/").map_err(|e| { + rustix::process::chdir("/").map_err(|e| { ChageError::UnexpectedFailure(format!("cannot chdir to / after chroot: {e}")) })?; @@ -536,7 +536,7 @@ where { // Consolidate real + effective UID to root for file operations. // Some filesystem configurations check real UID. - if nix::unistd::geteuid().is_root() { + if rustix::process::geteuid().is_root() { let _ = nix::unistd::setuid(nix::unistd::Uid::from_raw(0)); } diff --git a/src/uu/chfn/Cargo.toml b/src/uu/chfn/Cargo.toml index 93d8ffd..08b9255 100644 --- a/src/uu/chfn/Cargo.toml +++ b/src/uu/chfn/Cargo.toml @@ -21,6 +21,7 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } nix = { workspace = true } +rustix = { workspace = true } shadow-core = { workspace = true } uucore = { workspace = true } diff --git a/src/uu/chfn/src/chfn.rs b/src/uu/chfn/src/chfn.rs index 93429f3..8512434 100644 --- a/src/uu/chfn/src/chfn.rs +++ b/src/uu/chfn/src/chfn.rs @@ -140,10 +140,10 @@ fn do_chroot(dir: &str) -> Result<(), ChfnError> { } let path = std::path::Path::new(dir); - nix::unistd::chroot(path) + rustix::process::chroot(path) .map_err(|e| ChfnError::Error(format!("cannot chroot to '{dir}': {e}")))?; - nix::unistd::chdir("/") + rustix::process::chdir("/") .map_err(|e| ChfnError::Error(format!("cannot chdir to / after chroot: {e}")))?; Ok(()) @@ -160,7 +160,7 @@ where F: FnOnce(&mut PasswdEntry) -> Result<(), String>, { // Consolidate real + effective UID to root for file operations. - if nix::unistd::geteuid().is_root() { + if rustix::process::geteuid().is_root() { let _ = nix::unistd::setuid(nix::unistd::Uid::from_raw(0)); } diff --git a/src/uu/chpasswd/Cargo.toml b/src/uu/chpasswd/Cargo.toml index 96d46b1..a511ed0 100644 --- a/src/uu/chpasswd/Cargo.toml +++ b/src/uu/chpasswd/Cargo.toml @@ -21,6 +21,7 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } nix = { workspace = true } +rustix = { workspace = true } shadow-core = { workspace = true, features = ["shadow", "crypt"] } uucore = { workspace = true } zeroize = { workspace = true } diff --git a/src/uu/chpasswd/src/chpasswd.rs b/src/uu/chpasswd/src/chpasswd.rs index c8c0d37..aebb25f 100644 --- a/src/uu/chpasswd/src/chpasswd.rs +++ b/src/uu/chpasswd/src/chpasswd.rs @@ -304,7 +304,7 @@ fn apply_password_changes( hash_config: Option<&(shadow_core::crypt::CryptMethod, Option)>, ) -> UResult<()> { // Consolidate real + effective UID to root for file operations. - if nix::unistd::geteuid().is_root() { + if rustix::process::geteuid().is_root() { let _ = nix::unistd::setuid(nix::unistd::Uid::from_raw(0)); } @@ -419,10 +419,10 @@ fn do_chroot(dir: &str) -> Result<(), ChpasswdError> { } let path = Path::new(dir); - nix::unistd::chroot(path) + rustix::process::chroot(path) .map_err(|e| ChpasswdError::UnexpectedFailure(format!("cannot chroot to '{dir}': {e}")))?; - nix::unistd::chdir("/").map_err(|e| { + rustix::process::chdir("/").map_err(|e| { ChpasswdError::UnexpectedFailure(format!("cannot chdir to / after chroot: {e}")) })?; diff --git a/src/uu/chsh/Cargo.toml b/src/uu/chsh/Cargo.toml index 9532d2f..020bc95 100644 --- a/src/uu/chsh/Cargo.toml +++ b/src/uu/chsh/Cargo.toml @@ -21,6 +21,7 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } nix = { workspace = true } +rustix = { workspace = true } shadow-core = { workspace = true } uucore = { workspace = true } diff --git a/src/uu/chsh/src/chsh.rs b/src/uu/chsh/src/chsh.rs index ff5aeb0..af02636 100644 --- a/src/uu/chsh/src/chsh.rs +++ b/src/uu/chsh/src/chsh.rs @@ -80,10 +80,10 @@ fn do_chroot(dir: &str) -> Result<(), ChshError> { } let path = std::path::Path::new(dir); - nix::unistd::chroot(path) + rustix::process::chroot(path) .map_err(|e| ChshError::Error(format!("cannot chroot to '{dir}': {e}")))?; - nix::unistd::chdir("/") + rustix::process::chdir("/") .map_err(|e| ChshError::Error(format!("cannot chdir to / after chroot: {e}")))?; Ok(()) @@ -174,7 +174,7 @@ fn mutate_passwd(root: &SysRoot, username: &str, mutate: F) -> UResult<()> where F: FnOnce(&mut PasswdEntry) -> Result<(), String>, { - if nix::unistd::geteuid().is_root() { + if rustix::process::geteuid().is_root() { let _ = nix::unistd::setuid(nix::unistd::Uid::from_raw(0)); } diff --git a/src/uu/groupadd/Cargo.toml b/src/uu/groupadd/Cargo.toml index c87f409..3d31175 100644 --- a/src/uu/groupadd/Cargo.toml +++ b/src/uu/groupadd/Cargo.toml @@ -20,7 +20,7 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } -nix = { workspace = true } +rustix = { workspace = true } shadow-core = { workspace = true, features = ["shadow", "group", "gshadow", "login-defs"] } uucore = { workspace = true } diff --git a/src/uu/groupadd/src/groupadd.rs b/src/uu/groupadd/src/groupadd.rs index d809fcf..82e629d 100644 --- a/src/uu/groupadd/src/groupadd.rs +++ b/src/uu/groupadd/src/groupadd.rs @@ -456,7 +456,7 @@ mod tests { } fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() + !rustix::process::geteuid().is_root() } #[test] diff --git a/src/uu/groupdel/Cargo.toml b/src/uu/groupdel/Cargo.toml index 3312cef..e98a338 100644 --- a/src/uu/groupdel/Cargo.toml +++ b/src/uu/groupdel/Cargo.toml @@ -20,7 +20,7 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } -nix = { workspace = true } +rustix = { workspace = true } shadow-core = { workspace = true, features = ["shadow", "group", "gshadow", "login-defs"] } uucore = { workspace = true } diff --git a/src/uu/groupdel/src/groupdel.rs b/src/uu/groupdel/src/groupdel.rs index be5e737..8f09473 100644 --- a/src/uu/groupdel/src/groupdel.rs +++ b/src/uu/groupdel/src/groupdel.rs @@ -252,7 +252,7 @@ mod tests { } fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() + !rustix::process::geteuid().is_root() } #[test] diff --git a/src/uu/groupmod/Cargo.toml b/src/uu/groupmod/Cargo.toml index 79079c8..c0254b3 100644 --- a/src/uu/groupmod/Cargo.toml +++ b/src/uu/groupmod/Cargo.toml @@ -20,7 +20,7 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } -nix = { workspace = true } +rustix = { workspace = true } shadow-core = { workspace = true, features = ["shadow", "group", "gshadow", "login-defs"] } uucore = { workspace = true } diff --git a/src/uu/groupmod/src/groupmod.rs b/src/uu/groupmod/src/groupmod.rs index 82b4f29..1e91f11 100644 --- a/src/uu/groupmod/src/groupmod.rs +++ b/src/uu/groupmod/src/groupmod.rs @@ -329,7 +329,7 @@ mod tests { } fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() + !rustix::process::geteuid().is_root() } #[test] diff --git a/src/uu/grpck/Cargo.toml b/src/uu/grpck/Cargo.toml index 5aeaf33..6041110 100644 --- a/src/uu/grpck/Cargo.toml +++ b/src/uu/grpck/Cargo.toml @@ -20,8 +20,6 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } -libc = { workspace = true } -nix = { workspace = true } shadow-core = { workspace = true, features = ["shadow", "group", "gshadow", "login-defs"] } uucore = { workspace = true } diff --git a/src/uu/pwck/Cargo.toml b/src/uu/pwck/Cargo.toml index bd08e5a..bfefe2e 100644 --- a/src/uu/pwck/Cargo.toml +++ b/src/uu/pwck/Cargo.toml @@ -20,7 +20,6 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } -nix = { workspace = true } shadow-core = { workspace = true, features = ["shadow", "group", "login-defs"] } uucore = { workspace = true } diff --git a/src/uu/useradd/Cargo.toml b/src/uu/useradd/Cargo.toml index 568d30a..e10dccc 100644 --- a/src/uu/useradd/Cargo.toml +++ b/src/uu/useradd/Cargo.toml @@ -20,7 +20,7 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } -nix = { workspace = true } +rustix = { workspace = true } shadow-core = { workspace = true, features = ["shadow", "group", "gshadow", "login-defs", "subid"] } uucore = { workspace = true } diff --git a/src/uu/useradd/src/useradd.rs b/src/uu/useradd/src/useradd.rs index 1ef3ba3..c555d92 100644 --- a/src/uu/useradd/src/useradd.rs +++ b/src/uu/useradd/src/useradd.rs @@ -1594,7 +1594,7 @@ mod tests { /// Skip tests that require root privileges. fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() + !rustix::process::geteuid().is_root() } #[test] diff --git a/src/uu/userdel/Cargo.toml b/src/uu/userdel/Cargo.toml index fe50486..95328c2 100644 --- a/src/uu/userdel/Cargo.toml +++ b/src/uu/userdel/Cargo.toml @@ -20,7 +20,7 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } -nix = { workspace = true } +rustix = { workspace = true } shadow-core = { workspace = true, features = ["shadow", "group", "gshadow", "login-defs", "subid"] } uucore = { workspace = true } diff --git a/src/uu/userdel/src/userdel.rs b/src/uu/userdel/src/userdel.rs index 6d4362e..075c0f7 100644 --- a/src/uu/userdel/src/userdel.rs +++ b/src/uu/userdel/src/userdel.rs @@ -96,7 +96,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let root = SysRoot::new(prefix); // Must be root. - if !nix::unistd::getuid().is_root() { + if !rustix::process::getuid().is_root() { return Err(UserdelError::CantUpdatePasswd("Permission denied.".into()).into()); } @@ -434,7 +434,7 @@ mod tests { // Duplicated from tests/common/mod.rs — unit tests inside the crate // cannot import from the workspace-level tests directory. fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() + !rustix::process::geteuid().is_root() } #[test] diff --git a/src/uu/usermod/Cargo.toml b/src/uu/usermod/Cargo.toml index bca6c06..1370f3a 100644 --- a/src/uu/usermod/Cargo.toml +++ b/src/uu/usermod/Cargo.toml @@ -20,7 +20,7 @@ path = "src/main.rs" [dependencies] clap = { workspace = true } -nix = { workspace = true } +rustix = { workspace = true } shadow-core = { workspace = true, features = ["shadow", "group", "gshadow", "login-defs", "subid"] } uucore = { workspace = true } diff --git a/src/uu/usermod/src/usermod.rs b/src/uu/usermod/src/usermod.rs index 7bab695..733fee5 100644 --- a/src/uu/usermod/src/usermod.rs +++ b/src/uu/usermod/src/usermod.rs @@ -98,7 +98,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .map(Path::new); let root = SysRoot::new(prefix); - if !nix::unistd::getuid().is_root() { + if !rustix::process::getuid().is_root() { return Err(UsermodError::CantUpdate("Permission denied.".into()).into()); } @@ -331,7 +331,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { /// Uses `fchownat` with `AT_SYMLINK_NOFOLLOW` so symlinks themselves are /// re-owned without following them. fn recursive_chown(path: &Path, old_uid: u32, new_uid: u32) { - use nix::fcntl::AtFlags; use std::os::unix::fs::MetadataExt; if let Ok(entries) = std::fs::read_dir(path) { @@ -339,12 +338,12 @@ fn recursive_chown(path: &Path, old_uid: u32, new_uid: u32) { let entry_path = entry.path(); if let Ok(meta) = std::fs::symlink_metadata(&entry_path) { if meta.uid() == old_uid { - let _ = nix::unistd::fchownat( - nix::fcntl::AT_FDCWD, + let _ = rustix::fs::chownat( + rustix::fs::CWD, &entry_path, - Some(nix::unistd::Uid::from_raw(new_uid)), + Some(rustix::process::Uid::from_raw(new_uid)), None, - AtFlags::AT_SYMLINK_NOFOLLOW, + rustix::fs::AtFlags::SYMLINK_NOFOLLOW, ); } if meta.is_dir() { @@ -357,12 +356,12 @@ fn recursive_chown(path: &Path, old_uid: u32, new_uid: u32) { if let Ok(meta) = std::fs::symlink_metadata(path) && meta.uid() == old_uid { - let _ = nix::unistd::fchownat( - nix::fcntl::AT_FDCWD, + let _ = rustix::fs::chownat( + rustix::fs::CWD, path, - Some(nix::unistd::Uid::from_raw(new_uid)), + Some(rustix::process::Uid::from_raw(new_uid)), None, - AtFlags::AT_SYMLINK_NOFOLLOW, + rustix::fs::AtFlags::SYMLINK_NOFOLLOW, ); } } @@ -526,7 +525,7 @@ mod tests { } fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() + !rustix::process::geteuid().is_root() } #[test]