endpoint_sec/
audit.rs

1//! Expose a wrapper around [`audit_token_t`]: [`AuditToken`]
2
3use std::fmt;
4
5use endpoint_sec_sys::{
6    au_asid_t, audit_token_t, audit_token_to_asid, audit_token_to_auid, audit_token_to_egid, audit_token_to_euid,
7    audit_token_to_pid, audit_token_to_pidversion, audit_token_to_rgid, audit_token_to_ruid, gid_t, pid_t, uid_t,
8};
9
10/// A wrapper around an [`audit_token_t`].
11#[derive(Clone, Copy)]
12#[doc(alias = "audit_token_t")]
13#[repr(transparent)]
14pub struct AuditToken(pub audit_token_t);
15
16impl fmt::LowerHex for AuditToken {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        for v in self.0.val {
19            fmt::LowerHex::fmt(&v, f)?;
20        }
21
22        Ok(())
23    }
24}
25
26impl fmt::UpperHex for AuditToken {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        for v in self.0.val {
29            fmt::UpperHex::fmt(&v, f)?;
30        }
31
32        Ok(())
33    }
34}
35
36/// Endpoint Security wrappers and test helpers
37impl AuditToken {
38    /// Get the [`AuditToken`] for the given pid, if it exists.
39    ///
40    /// Endpoint Security does not currently provide a way to get the audit tokens for already
41    /// processes when first connecting a client, but it is relatively easy to list the PIDs of the
42    /// current processes.
43    ///
44    /// ## Implementation details
45    ///
46    /// Currently this method is implemented following the method [described here][method], with
47    /// calls to `task_name_for_pid` and `task_info(_, TASK_AUDIT_TOKEN, _, _)` but the first
48    /// function is marked as *obsolete* in the header containing it in macOS's SDK.
49    ///
50    /// Other possibilities could be `task_for_pid()` or `task_inspect_for_pid()`. For now the
51    /// current implementation is the most backward compatible. If you find a bug/need us to use a
52    /// more recent method, please signal it.
53    ///
54    /// [method]: https://developer.apple.com/forums/thread/652363
55    #[cfg(feature = "audit_token_from_pid")]
56    pub fn from_pid(pid: pid_t) -> Option<Self> {
57        let mut task_name = Default::default();
58        // Safety:
59        // - `mach_task_self` will always succeed
60        // - `task_name` is mutable and of the correct type so the reference is aligned and points
61        //   to initialized memory
62        // - result is checked below
63        let res = unsafe { mach2::traps::task_for_pid(mach2::traps::mach_task_self(), pid, &mut task_name) };
64        if res != libc::KERN_SUCCESS {
65            return None;
66        }
67
68        let mut audit_token = audit_token_t::default();
69        // Capacity in bytes of the array in `audit_token`
70        let mut cap = std::mem::size_of_val(&audit_token.val) as u32;
71        // Safety:
72        // - `task_name` is initialized
73        // - `audit_token` is mutable and of the correct type so the reference is aligned and points
74        //   to initialized memory, its type is in sync with `TASK_AUDIT_TOKEN` and `cap` is the
75        //   capacity in bytes
76        // - result is checked below
77        let res = unsafe {
78            libc::task_info(
79                task_name,
80                mach2::task_info::TASK_AUDIT_TOKEN,
81                audit_token.val.as_mut_ptr().cast(),
82                &mut cap,
83            )
84        };
85        if res != libc::KERN_SUCCESS {
86            return None;
87        }
88
89        Some(Self(audit_token))
90    }
91
92    /// Raw underlying audit token.
93    #[inline]
94    pub fn raw_token(&self) -> &audit_token_t {
95        &self.0
96    }
97
98    /// The audit user ID.
99    ///
100    /// **NOTE**: Used to identify Mach tasks and senders of Mach messages as subjects of the audit system.
101    #[inline(always)]
102    pub fn auid(&self) -> uid_t {
103        // Safety: The audit_token_t is owned by self.
104        unsafe { audit_token_to_auid(self.0) }
105    }
106
107    /// The effective user ID.
108    ///
109    /// **NOTE**: Used to identify Mach tasks and senders of Mach messages as subjects of the audit system.
110    #[inline(always)]
111    pub fn euid(&self) -> uid_t {
112        // Safety: The audit_token_t is owned by self.
113        unsafe { audit_token_to_euid(self.0) }
114    }
115
116    /// The effective group ID.
117    ///
118    /// **NOTE**: Used to identify Mach tasks and senders of Mach messages as subjects of the audit system.
119    #[inline(always)]
120    pub fn egid(&self) -> gid_t {
121        // Safety: The audit_token_t is owned by self.
122        unsafe { audit_token_to_egid(self.0) }
123    }
124
125    /// The real user ID.
126    ///
127    /// **NOTE**: Used to identify Mach tasks and senders of Mach messages as subjects of the audit system.
128    #[inline(always)]
129    pub fn ruid(&self) -> uid_t {
130        // Safety: The audit_token_t is owned by self.
131        unsafe { audit_token_to_ruid(self.0) }
132    }
133
134    /// The real group ID.
135    ///
136    /// **NOTE**: Used to identify Mach tasks and senders of Mach messages as subjects of the audit system.
137    #[inline(always)]
138    pub fn rgid(&self) -> gid_t {
139        // Safety: The audit_token_t is owned by self.
140        unsafe { audit_token_to_rgid(self.0) }
141    }
142
143    /// The process ID.
144    ///
145    /// **NOTE**: Used to identify Mach tasks and senders of Mach messages as subjects of the audit system.
146    #[inline(always)]
147    pub fn pid(&self) -> pid_t {
148        // Safety: The audit_token_t is owned by self.
149        unsafe { audit_token_to_pid(self.0) }
150    }
151
152    /// The audit session ID.
153    ///
154    /// **NOTE**: Used to identify Mach tasks and senders of Mach messages as subjects of the audit system.
155    #[inline(always)]
156    pub fn asid(&self) -> au_asid_t {
157        // Safety: The audit_token_t is owned by self.
158        unsafe { audit_token_to_asid(self.0) }
159    }
160
161    /// The process ID version.
162    ///
163    /// **NOTE**: Used to identify Mach tasks and senders of Mach messages as subjects of the audit system.
164    #[inline(always)]
165    pub fn pidversion(&self) -> i32 {
166        // Safety: The audit_token_t is owned by self.
167        unsafe { audit_token_to_pidversion(self.0) }
168    }
169}
170
171/// Crate-private methods
172impl AuditToken {
173    /// Create a new [`AuditToken`] from [`audit_token_t`].
174    #[inline(always)]
175    pub(crate) fn new(token: audit_token_t) -> Self {
176        AuditToken(token)
177    }
178
179    /// Allow to grab a reference out of the stored token.
180    #[inline(always)]
181    pub(crate) fn get_raw_ref(&self) -> &audit_token_t {
182        &self.0
183    }
184}
185
186static_assertions::assert_impl_all!(AuditToken: Send);
187
188impl_debug_eq_hash_with_functions!(
189    AuditToken;
190    auid,
191    euid,
192    egid,
193    ruid,
194    rgid,
195    pid,
196    asid,
197    pidversion,
198);
199
200#[cfg(test)]
201#[cfg(feature = "audit_token_from_pid")]
202mod test {
203    use sysinfo::{Pid, PidExt, ProcessExt, ProcessRefreshKind, System, SystemExt};
204
205    use super::*;
206
207    #[test]
208    fn audit_token_from_pid() {
209        let raw_pid = std::process::id();
210
211        let mut s = System::new();
212        s.refresh_process_specifics(Pid::from_u32(raw_pid), ProcessRefreshKind::everything());
213
214        let process = s.process(Pid::from_u32(raw_pid)).unwrap();
215
216        let audit = AuditToken::from_pid(raw_pid as i32).unwrap();
217
218        assert_eq!(process.user_id().map_or(0, |x| **x), audit.euid());
219        assert_eq!(process.group_id().map_or(0, |x| *x), audit.egid());
220        assert_eq!(process.pid().as_u32(), audit.pid() as u32);
221    }
222}