mirror of
https://github.com/uutils/shadow.git
synced 2026-06-10 16:14:57 -07:00
Merge pull request #146 from shadow-utils-rs/feat/140-nix-to-rustix-phase1
shadow-core, tools: migrate nix to rustix — phase 1 (#140)
This commit is contained in:
+2
-2
@@ -59,7 +59,7 @@ uucore = "0.7"
|
||||
thiserror = "2"
|
||||
|
||||
# Unix/Linux
|
||||
nix = { version = "0.30", features = ["user", "fs", "process", "signal", "term", "resource"] }
|
||||
rustix = { version = "1", features = ["process", "fs", "termios"] }
|
||||
libc = "0.2"
|
||||
|
||||
# Security
|
||||
@@ -205,7 +205,7 @@ usermod = { version = "0.0.1", package = "uu_usermod", path = "src/uu/usermod" }
|
||||
pwck = { version = "0.0.1", package = "uu_pwck", path = "src/uu/pwck" }
|
||||
grpck = { version = "0.0.1", package = "uu_grpck", path = "src/uu/grpck" }
|
||||
shadow-core = { path = "src/shadow-core", features = ["shadow"] }
|
||||
nix = { workspace = true }
|
||||
rustix = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -16,7 +16,7 @@ path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
libc = { workspace = true }
|
||||
nix = { workspace = true }
|
||||
rustix = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
subtle = { workspace = true }
|
||||
|
||||
@@ -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<std::rc::Rc<()>>,
|
||||
);
|
||||
struct UmaskGuard(rustix::fs::Mode, std::marker::PhantomData<std::rc::Rc<()>>);
|
||||
|
||||
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(())
|
||||
|
||||
@@ -14,8 +14,16 @@
|
||||
/// A core dump from a setuid-root process could expose password hashes
|
||||
/// and plaintext passwords.
|
||||
pub fn suppress_core_dumps() {
|
||||
let _ = nix::sys::resource::setrlimit(nix::sys::resource::Resource::RLIMIT_CORE, 0, 0);
|
||||
// PR_SET_DUMPABLE via nix::sys::prctl (no raw unsafe needed).
|
||||
use rustix::process::{Resource, Rlimit, setrlimit};
|
||||
|
||||
let _ = setrlimit(
|
||||
Resource::Core,
|
||||
Rlimit {
|
||||
current: Some(0),
|
||||
maximum: Some(0),
|
||||
},
|
||||
);
|
||||
// PR_SET_DUMPABLE via prctl (no raw unsafe needed).
|
||||
// nix doesn't expose prctl directly, so we skip it rather than use unsafe.
|
||||
// RLIMIT_CORE=0 is sufficient to prevent core dumps.
|
||||
}
|
||||
@@ -25,10 +33,14 @@ pub fn suppress_core_dumps() {
|
||||
/// A malicious caller could `ulimit -f 1` before invoking a setuid-root
|
||||
/// tool, causing `/etc/shadow` to be truncated mid-write.
|
||||
pub fn raise_file_size_limit() {
|
||||
let _ = nix::sys::resource::setrlimit(
|
||||
nix::sys::resource::Resource::RLIMIT_FSIZE,
|
||||
nix::sys::resource::RLIM_INFINITY,
|
||||
nix::sys::resource::RLIM_INFINITY,
|
||||
use rustix::process::{Resource, Rlimit, setrlimit};
|
||||
|
||||
let _ = setrlimit(
|
||||
Resource::Fsize,
|
||||
Rlimit {
|
||||
current: None,
|
||||
maximum: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,19 +133,35 @@ pub fn harden_process() -> Vec<(String, String)> {
|
||||
/// `geteuid()` is 0 for all callers, but the real UID identifies who
|
||||
/// actually invoked the program.
|
||||
pub fn caller_is_root() -> bool {
|
||||
nix::unistd::getuid().is_root()
|
||||
rustix::process::getuid().is_root()
|
||||
}
|
||||
|
||||
/// Return the current user's username from the real UID.
|
||||
pub fn current_username() -> Result<String, crate::error::ShadowError> {
|
||||
let uid = nix::unistd::getuid();
|
||||
match nix::unistd::User::from_uid(uid) {
|
||||
Ok(Some(user)) => Ok(user.name),
|
||||
Ok(None) => Err(crate::error::ShadowError::Other(
|
||||
let uid = rustix::process::getuid().as_raw();
|
||||
lookup_username_by_uid(uid)
|
||||
}
|
||||
|
||||
/// Look up a username by UID from `/etc/passwd`.
|
||||
pub fn lookup_username_by_uid(uid: u32) -> Result<String, crate::error::ShadowError> {
|
||||
let entries = crate::passwd::read_passwd_file(std::path::Path::new("/etc/passwd"))?;
|
||||
match entries.iter().find(|e| e.uid == uid) {
|
||||
Some(entry) => Ok(entry.name.clone()),
|
||||
None => Err(crate::error::ShadowError::Other(
|
||||
format!("cannot determine username for uid {uid}").into(),
|
||||
)),
|
||||
Err(e) => Err(crate::error::ShadowError::Other(
|
||||
format!("cannot determine username: {e}").into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Look up a passwd entry by UID from `/etc/passwd`.
|
||||
pub fn lookup_passwd_entry_by_uid(
|
||||
uid: u32,
|
||||
) -> Result<crate::passwd::PasswdEntry, crate::error::ShadowError> {
|
||||
let entries = crate::passwd::read_passwd_file(std::path::Path::new("/etc/passwd"))?;
|
||||
match entries.into_iter().find(|e| e.uid == uid) {
|
||||
Some(entry) => Ok(entry),
|
||||
None => Err(crate::error::ShadowError::Other(
|
||||
format!("no passwd entry for uid {uid}").into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -149,35 +177,22 @@ pub fn current_username() -> Result<String, crate::error::ShadowError> {
|
||||
/// inconsistent state or holding a stale lock. The original signal mask
|
||||
/// is restored when the guard is dropped.
|
||||
pub struct SignalBlocker {
|
||||
old_mask: nix::sys::signal::SigSet,
|
||||
saved: crate::process::SavedSigSet,
|
||||
}
|
||||
|
||||
impl SignalBlocker {
|
||||
/// Block `SIGINT`, `SIGTERM`, `SIGHUP` to prevent partial file writes.
|
||||
pub fn block_critical() -> Result<Self, crate::error::ShadowError> {
|
||||
use nix::sys::signal::{SigSet, SigmaskHow, Signal};
|
||||
let saved = crate::process::block_critical_signals().map_err(|e| {
|
||||
crate::error::ShadowError::Other(format!("cannot block signals: {e}").into())
|
||||
})?;
|
||||
|
||||
let mut block_set = SigSet::empty();
|
||||
block_set.add(Signal::SIGINT);
|
||||
block_set.add(Signal::SIGTERM);
|
||||
block_set.add(Signal::SIGHUP);
|
||||
|
||||
let mut old_mask = SigSet::empty();
|
||||
nix::sys::signal::sigprocmask(SigmaskHow::SIG_BLOCK, Some(&block_set), Some(&mut old_mask))
|
||||
.map_err(|e| {
|
||||
crate::error::ShadowError::Other(format!("cannot block signals: {e}").into())
|
||||
})?;
|
||||
|
||||
Ok(Self { old_mask })
|
||||
Ok(Self { saved })
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SignalBlocker {
|
||||
fn drop(&mut self) {
|
||||
let _ = nix::sys::signal::sigprocmask(
|
||||
nix::sys::signal::SigmaskHow::SIG_SETMASK,
|
||||
Some(&self.old_mask),
|
||||
None,
|
||||
);
|
||||
let _ = crate::process::restore_signals(&self.saved);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ pub mod crypt;
|
||||
#[cfg(feature = "selinux")]
|
||||
pub mod selinux;
|
||||
|
||||
// Process-level POSIX wrappers (setuid, sigprocmask, etc.) — FFI requires unsafe.
|
||||
#[allow(unsafe_code)]
|
||||
pub mod process;
|
||||
|
||||
pub mod atomic;
|
||||
pub mod audit;
|
||||
pub mod hardening;
|
||||
|
||||
+15
-10
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -296,6 +297,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_lock_file_has_cloexec() {
|
||||
use std::os::unix::io::AsFd;
|
||||
|
||||
// Rust's stdlib sets O_CLOEXEC by default on Linux.
|
||||
// Verify the lock file FD won't leak to child processes.
|
||||
let dir = tempfile::tempdir().expect("tempdir creation failed");
|
||||
@@ -305,9 +308,11 @@ 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");
|
||||
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");
|
||||
let flags = rustix::io::fcntl_getfd(f.as_fd()).expect("fcntl F_GETFD failed");
|
||||
assert!(
|
||||
flags.contains(rustix::io::FdFlags::CLOEXEC),
|
||||
"FD should have CLOEXEC set"
|
||||
);
|
||||
|
||||
lock.release().expect("failed to release lock");
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ fn prompt_for_input(
|
||||
///
|
||||
/// Opens `/dev/tty` once with read+write mode (not stdin) to ensure we talk
|
||||
/// to the real terminal even if stdin has been redirected. Uses
|
||||
/// `nix::sys::termios` to disable `ECHO` for password prompts and restores
|
||||
/// `rustix::termios` to disable `ECHO` for password prompts and restores
|
||||
/// the original settings afterward (including on error, via a drop guard).
|
||||
fn read_from_tty(message: &PamMessage, echo: bool) -> io::Result<zeroize::Zeroizing<String>> {
|
||||
let mut tty = File::options().read(true).write(true).open("/dev/tty")?;
|
||||
@@ -482,26 +482,28 @@ fn free_responses(responses: *mut PamResponse, count: usize) {
|
||||
|
||||
/// RAII guard that disables terminal echo and restores it on drop.
|
||||
///
|
||||
/// Uses `nix::sys::termios` to manipulate the terminal's local flags. The
|
||||
/// Uses `rustix::termios` to manipulate the terminal's local flags. The
|
||||
/// original settings are saved and restored when the guard is dropped, even
|
||||
/// if the caller returns early or panics.
|
||||
struct EchoGuard {
|
||||
fd: libc::c_int,
|
||||
original: nix::sys::termios::Termios,
|
||||
original: rustix::termios::Termios,
|
||||
}
|
||||
|
||||
impl EchoGuard {
|
||||
/// Disable echo on the given terminal file.
|
||||
fn disable(tty: &File) -> io::Result<Self> {
|
||||
use nix::sys::termios::{self, LocalFlags, SetArg};
|
||||
use std::os::unix::io::AsFd;
|
||||
|
||||
let fd = tty.as_raw_fd();
|
||||
let original = termios::tcgetattr(tty).map_err(io::Error::other)?;
|
||||
let original = rustix::termios::tcgetattr(tty.as_fd()).map_err(io::Error::other)?;
|
||||
|
||||
let mut noecho = original.clone();
|
||||
noecho.local_flags &= !(LocalFlags::ECHO | LocalFlags::ECHONL);
|
||||
noecho.local_modes &=
|
||||
!(rustix::termios::LocalModes::ECHO | rustix::termios::LocalModes::ECHONL);
|
||||
|
||||
termios::tcsetattr(tty, SetArg::TCSANOW, &noecho).map_err(io::Error::other)?;
|
||||
rustix::termios::tcsetattr(tty.as_fd(), rustix::termios::OptionalActions::Now, &noecho)
|
||||
.map_err(io::Error::other)?;
|
||||
|
||||
Ok(Self { fd, original })
|
||||
}
|
||||
@@ -516,7 +518,7 @@ impl Drop for EchoGuard {
|
||||
use std::os::unix::io::BorrowedFd;
|
||||
let fd = unsafe { BorrowedFd::borrow_raw(self.fd) };
|
||||
let _ =
|
||||
nix::sys::termios::tcsetattr(fd, nix::sys::termios::SetArg::TCSANOW, &self.original);
|
||||
rustix::termios::tcsetattr(fd, rustix::termios::OptionalActions::Now, &self.original);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
// This file is part of the shadow-rs package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
// spell-checker:ignore setuid seteuid setgid initgroups sigprocmask
|
||||
|
||||
//! Process-level POSIX wrappers for setuid-root tools.
|
||||
//!
|
||||
//! These functions call libc directly because rustix intentionally does not
|
||||
//! provide process-wide `setuid`/`setgid`/`sigprocmask` (they require libc
|
||||
//! coordination for thread safety). The `libc` crate is already a dependency
|
||||
//! for PAM FFI.
|
||||
//!
|
||||
//! This is one of the few modules that permits `unsafe` — all unsafe is
|
||||
//! confined to well-understood POSIX C library calls.
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::io;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// UID / GID manipulation (process-wide via libc)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `setuid(uid)` — set the real and effective user ID of the calling process.
|
||||
///
|
||||
/// This calls the libc `setuid()` which is process-wide (unlike the raw
|
||||
/// syscall which is per-thread on Linux).
|
||||
pub fn setuid(uid: u32) -> io::Result<()> {
|
||||
// SAFETY: setuid is a standard POSIX function. The only precondition
|
||||
// is that uid is a valid UID value, which u32 always satisfies.
|
||||
let ret = unsafe { libc::setuid(uid) };
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
/// `seteuid(uid)` — set the effective user ID of the calling process.
|
||||
///
|
||||
/// This calls the libc `seteuid()` which is process-wide.
|
||||
pub fn seteuid(uid: u32) -> io::Result<()> {
|
||||
// SAFETY: seteuid is a standard POSIX function.
|
||||
let ret = unsafe { libc::seteuid(uid) };
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
/// `setgid(gid)` — set the real and effective group ID of the calling process.
|
||||
///
|
||||
/// This calls the libc `setgid()` which is process-wide.
|
||||
pub fn setgid(gid: u32) -> io::Result<()> {
|
||||
// SAFETY: setgid is a standard POSIX function.
|
||||
let ret = unsafe { libc::setgid(gid) };
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
/// `initgroups(user, gid)` — initialize the supplementary group list.
|
||||
///
|
||||
/// Sets the supplementary groups for `user` plus `gid`.
|
||||
pub fn initgroups(user: &CStr, gid: u32) -> io::Result<()> {
|
||||
// SAFETY: initgroups is a standard POSIX function. `user` is a valid
|
||||
// null-terminated CStr.
|
||||
let ret = unsafe { libc::initgroups(user.as_ptr(), gid) };
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// exec
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `execv(path, argv)` — replace the current process image.
|
||||
///
|
||||
/// On success this function never returns. On failure it returns an error.
|
||||
pub fn execv(path: &CStr, argv: &[&CStr]) -> io::Error {
|
||||
// Build a null-terminated array of pointers for execv.
|
||||
let mut argv_ptrs: Vec<*const libc::c_char> = argv.iter().map(|s| s.as_ptr()).collect();
|
||||
argv_ptrs.push(std::ptr::null());
|
||||
|
||||
// SAFETY: execv is a standard POSIX function. The argv array is
|
||||
// null-terminated and all CStr pointers are valid.
|
||||
unsafe {
|
||||
libc::execv(path.as_ptr(), argv_ptrs.as_ptr());
|
||||
}
|
||||
// execv only returns on error.
|
||||
io::Error::last_os_error()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Signal blocking (process-wide via libc)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A saved signal mask, used by [`block_signals`] and [`restore_signals`].
|
||||
///
|
||||
/// Wraps a `libc::sigset_t`.
|
||||
pub struct SavedSigSet {
|
||||
set: libc::sigset_t,
|
||||
}
|
||||
|
||||
/// Block `SIGINT`, `SIGTERM`, `SIGHUP` and return the previous signal mask.
|
||||
///
|
||||
/// Prevents these signals from interrupting a lock-modify-write sequence.
|
||||
pub fn block_critical_signals() -> io::Result<SavedSigSet> {
|
||||
// SAFETY: sigemptyset, sigaddset, and sigprocmask are standard POSIX
|
||||
// functions. We initialize the sigset_t with sigemptyset before use.
|
||||
unsafe {
|
||||
let mut block_set: libc::sigset_t = std::mem::zeroed();
|
||||
libc::sigemptyset(&raw mut block_set);
|
||||
libc::sigaddset(&raw mut block_set, libc::SIGINT);
|
||||
libc::sigaddset(&raw mut block_set, libc::SIGTERM);
|
||||
libc::sigaddset(&raw mut block_set, libc::SIGHUP);
|
||||
|
||||
let mut old_set: libc::sigset_t = std::mem::zeroed();
|
||||
let ret = libc::sigprocmask(libc::SIG_BLOCK, &raw const block_set, &raw mut old_set);
|
||||
if ret != 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(SavedSigSet { set: old_set })
|
||||
}
|
||||
}
|
||||
|
||||
/// Restore a previously saved signal mask.
|
||||
pub fn restore_signals(saved: &SavedSigSet) -> io::Result<()> {
|
||||
// SAFETY: sigprocmask with SIG_SETMASK restores a previously captured mask.
|
||||
let ret = unsafe {
|
||||
libc::sigprocmask(
|
||||
libc::SIG_SETMASK,
|
||||
&raw const saved.set,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
nix = { workspace = true }
|
||||
rustix = { workspace = true }
|
||||
shadow-core = { workspace = true, features = ["shadow"] }
|
||||
uucore = { workspace = true }
|
||||
|
||||
|
||||
@@ -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,8 +536,8 @@ where
|
||||
{
|
||||
// Consolidate real + effective UID to root for file operations.
|
||||
// Some filesystem configurations check real UID.
|
||||
if nix::unistd::geteuid().is_root() {
|
||||
let _ = nix::unistd::setuid(nix::unistd::Uid::from_raw(0));
|
||||
if rustix::process::geteuid().is_root() {
|
||||
let _ = shadow_core::process::setuid(0);
|
||||
}
|
||||
|
||||
// Block signals for the entire critical section (lock -> write -> unlock).
|
||||
|
||||
@@ -20,7 +20,7 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
nix = { workspace = true }
|
||||
rustix = { workspace = true }
|
||||
shadow-core = { workspace = true }
|
||||
uucore = { workspace = true }
|
||||
|
||||
|
||||
@@ -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,8 +160,8 @@ where
|
||||
F: FnOnce(&mut PasswdEntry) -> Result<(), String>,
|
||||
{
|
||||
// Consolidate real + effective UID to root for file operations.
|
||||
if nix::unistd::geteuid().is_root() {
|
||||
let _ = nix::unistd::setuid(nix::unistd::Uid::from_raw(0));
|
||||
if rustix::process::geteuid().is_root() {
|
||||
let _ = shadow_core::process::setuid(0);
|
||||
}
|
||||
|
||||
let _signals = shadow_core::hardening::SignalBlocker::block_critical()
|
||||
|
||||
@@ -20,7 +20,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 }
|
||||
|
||||
@@ -304,8 +304,8 @@ fn apply_password_changes(
|
||||
hash_config: Option<&(shadow_core::crypt::CryptMethod, Option<u32>)>,
|
||||
) -> UResult<()> {
|
||||
// Consolidate real + effective UID to root for file operations.
|
||||
if nix::unistd::geteuid().is_root() {
|
||||
let _ = nix::unistd::setuid(nix::unistd::Uid::from_raw(0));
|
||||
if rustix::process::geteuid().is_root() {
|
||||
let _ = shadow_core::process::setuid(0);
|
||||
}
|
||||
|
||||
// Block signals for the entire critical section.
|
||||
@@ -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}"))
|
||||
})?;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
nix = { workspace = true }
|
||||
rustix = { workspace = true }
|
||||
shadow-core = { workspace = true }
|
||||
uucore = { workspace = true }
|
||||
|
||||
|
||||
@@ -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,8 +174,8 @@ fn mutate_passwd<F>(root: &SysRoot, username: &str, mutate: F) -> UResult<()>
|
||||
where
|
||||
F: FnOnce(&mut PasswdEntry) -> Result<(), String>,
|
||||
{
|
||||
if nix::unistd::geteuid().is_root() {
|
||||
let _ = nix::unistd::setuid(nix::unistd::Uid::from_raw(0));
|
||||
if rustix::process::geteuid().is_root() {
|
||||
let _ = shadow_core::process::setuid(0);
|
||||
}
|
||||
|
||||
let _signals = shadow_core::hardening::SignalBlocker::block_critical()
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -456,7 +456,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn skip_unless_root() -> bool {
|
||||
!nix::unistd::geteuid().is_root()
|
||||
!rustix::process::geteuid().is_root()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user