Skip to content

Commit 8cdd8fe

Browse files
committed
deescalate privilege to create and open files
1 parent 564524e commit 8cdd8fe

3 files changed

Lines changed: 54 additions & 28 deletions

File tree

src/cli.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
use anyhow::Result;
22
use clap::{Parser, Subcommand};
33
use std::{
4-
fs::{self, File},
4+
fs::{self, OpenOptions},
55
io::{self, BufWriter, Write},
6-
os::unix::fs::chown,
7-
path::{Path, PathBuf},
6+
path::PathBuf,
87
};
98

10-
use crate::invoker_permissions;
9+
use crate::utils::PrivGuard;
1110

1211
#[derive(Parser, Debug)]
1312
#[command(name = "trace_v3", arg_required_else_help = true)]
@@ -56,12 +55,7 @@ fn make_output(target: &str) -> Result<Output> {
5655
match target {
5756
"-" => Ok(Box::new(io::stdout())),
5857
path => {
59-
let (uid, gid) = invoker_permissions()?;
60-
61-
let set_owner = |path: &Path| {
62-
chown(path, Some(uid), Some(gid))
63-
.expect("Failed to change file ownership to non-sudo user");
64-
};
58+
let _guard = PrivGuard::drop_to_user()?;
6559

6660
let mut file_path = PathBuf::from(path);
6761

@@ -70,14 +64,17 @@ fn make_output(target: &str) -> Result<Output> {
7064
}
7165

7266
if let Some(parent) = file_path.parent() {
73-
if !parent.exists() {
67+
if !parent.as_os_str().is_empty() {
7468
fs::create_dir_all(parent)?;
75-
set_owner(&parent);
7669
}
7770
}
7871

79-
let file = File::create(&file_path)?;
80-
set_owner(&file_path);
72+
let file = OpenOptions::new()
73+
.write(true)
74+
.create(true)
75+
.truncate(true)
76+
.open(&path)?;
77+
8178
Ok(Box::new(BufWriter::new(file)))
8279
}
8380
}

src/main.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,29 @@ use libc::{
55
c_int, kill, sigaction, sigaddset, sigemptyset, sighandler_t, sigprocmask, sigset_t, sigwait,
66
waitpid, SA_NOCLDSTOP, SA_RESTART, SIGCHLD, SIGUSR1, SIG_BLOCK, SIG_UNBLOCK,
77
};
8-
use nix::unistd::{getgid, getuid, setgroups, setresgid, setresuid, Gid, Uid};
8+
use nix::unistd::{setgroups, setresgid, setresuid, Gid, Uid};
99
use std::ffi::CStr;
1010
use std::ffi::CString;
1111
use std::io::{Error, ErrorKind};
1212
use std::mem::{size_of, zeroed, MaybeUninit};
1313
use std::os::raw::c_char;
1414
use std::os::unix::io::RawFd;
15+
use std::ptr;
1516
use std::sync::mpsc;
1617
use std::thread;
1718
use std::time::Duration;
18-
use std::{env, ptr};
1919

2020
mod cli;
2121
mod dep_tracer;
2222
mod installer;
23+
mod utils;
2324

2425
use crate::cli::{Cli, Commands, Outputs};
2526
use crate::dep_tracer::event_stream_handler;
2627
use crate::dep_tracer::SyscallEvent;
2728
use crate::dep_tracer::{CTXT, LOGS, SETS};
2829
use crate::installer::installer;
30+
use crate::utils::invoker_permissions;
2931
use trace_v3::sys_enter_info_t;
3032
use trace_v3::sys_exit_info_t;
3133

@@ -36,18 +38,6 @@ extern "C" fn sigchld_handler(_sig: i32) {
3638
RUNNING.store(false, Ordering::Relaxed);
3739
}
3840

39-
fn invoker_permissions() -> Result<(u32, u32)> {
40-
let uid = match env::var("SUDO_UID").ok() {
41-
Some(uid) => uid.parse()?,
42-
None => getuid().as_raw(), // if invoking with setuid, use ruid
43-
};
44-
let gid = match env::var("SUDO_GID").ok() {
45-
Some(gid) => gid.parse()?,
46-
None => getgid().as_raw(), // if invoking with setuid, use rgid
47-
};
48-
Ok((uid, gid))
49-
}
50-
5141
fn monitor_pid(pid: i32) -> std::io::Result<RawFd> {
5242
unsafe {
5343
let fd = libc::syscall(libc::SYS_pidfd_open, pid, 0);

src/utils.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use anyhow::Result;
2+
use nix::unistd::{geteuid, getgid, getuid, setresuid, Uid};
3+
use std::{env, io};
4+
5+
pub fn invoker_permissions() -> Result<(u32, u32)> {
6+
let uid = match env::var("SUDO_UID").ok() {
7+
Some(uid) => uid.parse()?,
8+
None => getuid().as_raw(), // if invoking with setuid, use ruid
9+
};
10+
let gid = match env::var("SUDO_GID").ok() {
11+
Some(gid) => gid.parse()?,
12+
None => getgid().as_raw(), // if invoking with setuid, use rgid
13+
};
14+
Ok((uid, gid))
15+
}
16+
17+
/// Safely drops effective privileges to the Real User ID while held.
18+
/// Restores them to the Saved UID (root) when dropped.
19+
pub struct PrivGuard {
20+
saved_euid: Uid,
21+
}
22+
23+
impl PrivGuard {
24+
pub fn drop_to_user() -> Result<Self> {
25+
let ruid = getuid();
26+
let euid = geteuid();
27+
// setresuid(real, effective, saved)
28+
setresuid(ruid, ruid, euid).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
29+
Ok(PrivGuard { saved_euid: euid })
30+
}
31+
}
32+
33+
impl Drop for PrivGuard {
34+
fn drop(&mut self) {
35+
let ruid = getuid();
36+
// Restore effective UID to the saved UID (root)
37+
let _ = setresuid(ruid, self.saved_euid, self.saved_euid);
38+
}
39+
}

0 commit comments

Comments
 (0)