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}