tracexec_core/
tracee.rs

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