1
//! Utility functions for the rest of the crate.
2

            
3
use tor_error::error_report;
4
use tor_persist::StateMgr;
5

            
6
/// A RAII guard that calls `<T as StateMgr>::unlock` on drop.
7
pub(crate) struct StateMgrUnlockGuard<'a, T: StateMgr + 'a> {
8
    /// The inner manager.
9
    mgr: &'a T,
10
}
11

            
12
impl<'a, T: StateMgr + 'a> Drop for StateMgrUnlockGuard<'a, T> {
13
    fn drop(&mut self) {
14
        if let Err(e) = self.mgr.unlock() {
15
            error_report!(e, "Failed to unlock state manager");
16
        }
17
    }
18
}
19

            
20
impl<'a, T: StateMgr + 'a> StateMgrUnlockGuard<'a, T> {
21
    /// Create an unlock guard.
22
    pub(crate) fn new(mgr: &'a T) -> Self {
23
        Self { mgr }
24
    }
25
    /// Consume the unlock guard without unlocking the state manager.
26
    pub(crate) fn disarm(self) {
27
        std::mem::forget(self);
28
    }
29
}
30

            
31
/// Return true if we are running with elevated privileges via setuid, setgid,
32
/// or a similar mechanism.
33
///
34
/// We detect this by checking whether there is any difference between our real,
35
/// effective, and saved user IDs; and then by doing the same check for our
36
/// group IDs.
37
///
38
/// On non-unix platforms, this function always returns false.
39
40
pub(crate) fn running_as_setuid() -> bool {
40
40
    cfg_if::cfg_if! {
41
40
        // this is the exhaustive list of os where `getresuid` and `getresgid` are available
42
40
        if #[cfg(any(target_os = "fuchsia",
43
40
                     target_os = "linux",
44
40
                     target_os = "l4re",
45
40
                     target_os = "android",
46
40
                     target_os = "emscripten",
47
40
                     target_os = "freebsd",
48
40
                     target_os = "dragonfly",
49
40
                     target_os = "openbsd",
50
40
                     ))] {
51
40
            // Use `libc` to find our real, effective, and saved UIDs and GIDs.
52
40
            //
53
40
            // On systems with setresuid, a caller can (and people sometimes do)
54
40
            // arrange for ruid==euid != saveduid, and that is still set-id:
55
40
            // the process can retain privilege.
56
40
            let mut resuid = [0, 0, 0];
57
40
            let mut resgid = [0, 0, 0];
58
40
            unsafe {
59
40
                // We ignore failures from getresuid or getresgid: these syscalls
60
40
                // can only fail if we give them bad pointers, if they are disabled
61
40
                // via a maddened sandbox, or something like that.  In that case, we'll
62
40
                // just assume that the user knows what they're doing.
63
40
                let _ = libc::getresuid(&mut resuid[0], &mut resuid[1], &mut resuid[2]);
64
40
                let _ = libc::getresgid(&mut resgid[0], &mut resgid[1], &mut resgid[2]);
65
40
            }
66
40

            
67
40
            // The user can change any of their (real, effective, saved) IDs to any
68
40
            // of their (real, effective, saved) IDs.  Thus, privileges are elevated
69
40
            // if there is any difference between these IDs.
70
124
            let same_resuid = resuid.iter().all(|uid| uid == &resuid[0]);
71
124
            let same_resgid = resgid.iter().all(|gid| gid == &resgid[0]);
72
40
            !(same_resuid && same_resgid)
73
        } else if #[cfg(any(target_family = "unix", target_os = "vxworks"))] {
74
            // Some unix-like OS lacks setresuid/setresgid, while possibly still having a saved
75
            // set-id, which we then can't query. I *think* it is even possible to create a
76
            // situation where ruid==euid != saveduid.  But it is not possible to detect it
77
            // (other than maybe by experimentally guessing that saved set-id is zero and trying
78
            // to seteuid(0) - a bad move).
79
            //
80
            // So, use test for euid==ruid instead, which is about the best we can do.
81

            
82
            // We "sort of" ignore failures: in each case, we insist that both calls succeed,
83
            // giving the same answer, or both calls fail.  This ought to work well enough.
84
            let same_reuid = unsafe { libc::geteuid() == libc::getuid() };
85
            let same_regid = unsafe { libc::getegid() == libc::getgid() };
86
            !(same_reuid && same_regid)
87
        } else {
88
            false
89
        }
90
    }
91
40
}