Skip to main content

tracexec_core/
tracee.rs

1//! Common operations to run in tracee process
2
3use std::os::fd::{
4  AsFd,
5  FromRawFd,
6  OwnedFd,
7};
8
9use nix::{
10  errno::Errno,
11  libc,
12  unistd::{
13    Gid,
14    Uid,
15    User,
16    dup2,
17    getpid,
18    setpgid,
19    setresgid,
20    setresuid,
21    setsid,
22  },
23};
24
25pub fn nullify_stdio() -> Result<(), std::io::Error> {
26  let dev_null = std::fs::File::options()
27    .read(true)
28    .write(true)
29    .open("/dev/null")?;
30  let mut stdin = unsafe { OwnedFd::from_raw_fd(0) };
31  let mut stdout = unsafe { OwnedFd::from_raw_fd(1) };
32  let mut stderr = unsafe { OwnedFd::from_raw_fd(2) };
33  dup2(dev_null.as_fd(), &mut stdin)?;
34  dup2(dev_null.as_fd(), &mut stdout)?;
35  dup2(dev_null.as_fd(), &mut stderr)?;
36  std::mem::forget(stdin);
37  std::mem::forget(stdout);
38  std::mem::forget(stderr);
39  Ok(())
40}
41
42pub fn runas(user: &User, effective: Option<(Uid, Gid)>) -> Result<(), Errno> {
43  let (euid, egid) = effective.unwrap_or((user.uid, user.gid));
44  do_initgroups(&user.name, user.gid)?;
45  setresgid(user.gid, egid, Gid::from_raw(u32::MAX))?;
46  setresuid(user.uid, euid, Uid::from_raw(u32::MAX))?;
47  Ok(())
48}
49
50/// Set supplementary groups by reading `/etc/group` directly,
51/// avoiding dynamic NSS which crashes in static glibc builds.
52#[cfg(all(target_env = "gnu", target_feature = "crt-static"))]
53fn do_initgroups(username: &str, primary_gid: Gid) -> Result<(), Errno> {
54  let gids = crate::account::supplementary_gids(username, primary_gid)?;
55  nix::unistd::setgroups(&gids)
56}
57
58/// Use the standard `initgroups` from libc for non-static-glibc builds.
59#[cfg(not(all(target_env = "gnu", target_feature = "crt-static")))]
60fn do_initgroups(username: &str, primary_gid: Gid) -> Result<(), Errno> {
61  nix::unistd::initgroups(
62    &std::ffi::CString::new(username).map_err(|_| Errno::EINVAL)?,
63    primary_gid,
64  )
65}
66
67pub fn lead_process_group() -> Result<(), Errno> {
68  let me = getpid();
69  setpgid(me, me)
70}
71
72pub fn lead_session_and_control_terminal() -> Result<(), Errno> {
73  setsid()?;
74  if unsafe { libc::ioctl(0, libc::TIOCSCTTY as _, 0) } == -1 {
75    Err(Errno::last())?;
76  }
77  Ok(())
78}
79
80#[cfg(test)]
81mod tests {
82  use std::io::{
83    Read,
84    Write,
85  };
86
87  use nix::unistd::getpgrp;
88  use rusty_fork::rusty_fork_test;
89
90  use super::*;
91
92  rusty_fork_test! {
93    #[test]
94    fn test_nullify_stdio() {
95      nullify_stdio().expect("nullify_stdio failed");
96
97      // stdout should now point to /dev/null:
98      // write should succeed
99      let mut stdout = std::io::stdout();
100      stdout.write_all(b"discarded").unwrap();
101      stdout.flush().unwrap();
102
103      // stdin should read EOF
104      let mut buf = [0u8; 16];
105      let mut stdin = std::io::stdin();
106      let n = stdin.read(&mut buf).unwrap();
107      assert_eq!(n, 0);
108    }
109  }
110
111  rusty_fork_test! {
112    #[test]
113    fn test_lead_process_group() {
114      let pid = nix::unistd::getpid();
115      let pgrp_before = getpgrp();
116
117      lead_process_group().expect("lead_process_group failed");
118
119      let pgrp_after = getpgrp();
120
121      // We should now be our own process group leader
122      assert_eq!(pgrp_after, pid);
123
124      // Ensure we actually changed if not already leader
125      let _ = pgrp_before;
126    }
127  }
128}