Skip to main content

endpoint_sec/event/
event_authentication.rs

1//! [`EventAuthentication`]
2
3use std::ffi::OsStr;
4
5use endpoint_sec_sys::{
6    es_authentication_type_t, es_auto_unlock_type_t, es_event_authentication_auto_unlock_t,
7    es_event_authentication_od_t, es_event_authentication_t, es_event_authentication_t_anon0,
8    es_event_authentication_token_t, es_event_authentication_touchid_t, es_touchid_mode_t, uid_t,
9};
10
11use crate::{AuditToken, Process};
12
13/// An authentication was performed.
14#[doc(alias = "es_event_authentication_t")]
15pub struct EventAuthentication<'a> {
16    /// Raw event
17    pub(crate) raw: &'a es_event_authentication_t,
18    /// Message version
19    pub(crate) version: u32,
20}
21
22impl<'a> EventAuthentication<'a> {
23    /// True iff authentication was successful.
24    #[inline(always)]
25    pub fn success(&self) -> bool {
26        self.raw.success
27    }
28
29    /// The type of authentication.
30    #[inline(always)]
31    pub fn type_(&self) -> es_authentication_type_t {
32        self.raw.type_
33    }
34
35    /// Type-specific data describing the authentication.
36    #[inline(always)]
37    pub fn raw_data(&self) -> &'a es_event_authentication_t_anon0 {
38        &self.raw.data
39    }
40
41    /// Details about event
42    #[inline(always)]
43    pub fn data(&self) -> Option<AuthenticationData<'a>> {
44        let res = match self.type_() {
45            es_authentication_type_t::ES_AUTHENTICATION_TYPE_OD => AuthenticationData::Od(EventAuthenticationOd {
46                // Safety: access to union is gated on relevant enum
47                raw: unsafe { self.raw_data().od.as_opt()? },
48                version: self.version,
49            }),
50            es_authentication_type_t::ES_AUTHENTICATION_TYPE_TOUCHID => {
51                AuthenticationData::TouchId(EventAuthenticationTouchId {
52                    // Safety: access to union is gated on relevant enum
53                    raw: unsafe { self.raw_data().touchid.as_opt()? },
54                    version: self.version,
55                    success: self.success(),
56                })
57            },
58            es_authentication_type_t::ES_AUTHENTICATION_TYPE_TOKEN => {
59                AuthenticationData::Token(EventAuthenticationToken {
60                    // Safety: access to union is gated on relevant enum
61                    raw: unsafe { self.raw_data().token.as_opt()? },
62                    version: self.version,
63                })
64            },
65            es_authentication_type_t::ES_AUTHENTICATION_TYPE_AUTO_UNLOCK => {
66                AuthenticationData::AutoUnlock(EventAuthenticationAutoUnlock {
67                    // Safety: access to union is gated on relevant enum
68                    raw: unsafe { self.raw_data().auto_unlock.as_opt()? },
69                })
70            },
71            _ => return None,
72        };
73        Some(res)
74    }
75}
76
77// Safety: safe to send across threads: does not contain any interior mutability nor depend on current thread state
78unsafe impl Send for EventAuthentication<'_> {}
79// Safety: safe to share across threads: does not contain any interior mutability nor depend on current thread state
80unsafe impl Sync for EventAuthentication<'_> {}
81
82impl_debug_eq_hash_with_functions!(EventAuthentication<'a>; success, type_, data);
83
84/// See [`es_event_authentication_t_anon0`]
85#[doc(alias = "es_event_authentication_t_anon0")]
86#[doc(alias = "es_authentication_type_t")]
87#[derive(Debug, PartialEq, Eq, Hash)]
88#[non_exhaustive]
89pub enum AuthenticationData<'a> {
90    /// Wrapped [`es_event_authentication_t_anon_0.od`]
91    Od(EventAuthenticationOd<'a>),
92    /// Wrapped [`es_event_authentication_t_anon_0.touchid`]
93    TouchId(EventAuthenticationTouchId<'a>),
94    /// Wrapped [`es_event_authentication_t_anon_0.token`]
95    Token(EventAuthenticationToken<'a>),
96    /// Wrapped [`es_event_authentication_t_anon_0.auto_unlock`]
97    AutoUnlock(EventAuthenticationAutoUnlock<'a>),
98}
99
100/// OpenDirectory authentication data
101#[doc(alias = "es_event_authentication_od_t")]
102pub struct EventAuthenticationOd<'a> {
103    /// Raw event
104    raw: &'a es_event_authentication_od_t,
105    /// Message version
106    version: u32,
107}
108
109impl<'a> EventAuthenticationOd<'a> {
110    /// Process that instigated the authentication (XPC caller that asked for authentication).
111    #[inline(always)]
112    pub fn instigator(&self) -> Option<Process<'a>> {
113        // Safety: 'a tied to self, object obtained through ES
114        let process = unsafe { self.raw.instigator()? };
115        Some(Process::new(process, self.version))
116    }
117
118    /// Audit token of the process that instigated this event.
119    pub fn instigator_token(&self) -> AuditToken {
120        #[cfg(feature = "macos_15_0_0")]
121        if self.version >= 8 {
122            return AuditToken(self.raw.instigator_token);
123        }
124
125        // On old versions, the process was always non-null, and we can get
126        // its token easily.
127        self.instigator().unwrap().audit_token()
128    }
129
130    /// OD record type against which OD is authenticating. Typically `Users`, but other record types
131    /// can auth too.
132    #[inline(always)]
133    pub fn record_type(&self) -> &'a OsStr {
134        // Safety: 'a tied to self, object obtained through ES
135        unsafe { self.raw.record_type.as_os_str() }
136    }
137
138    /// OD record name against which OD is authenticating. For record type `Users`, this is the
139    /// username.
140    #[inline(always)]
141    pub fn record_name(&self) -> &'a OsStr {
142        // Safety: 'a tied to self, object obtained through ES
143        unsafe { self.raw.record_name.as_os_str() }
144    }
145
146    /// OD node against which OD is authenticating. Typically one of `/Local/Default`, `/LDAPv3/
147    /// <server>` or `/Active Directory/<domain>`.
148    #[inline(always)]
149    pub fn node_name(&self) -> &'a OsStr {
150        // Safety: 'a tied to self, object obtained through ES
151        unsafe { self.raw.node_name.as_os_str() }
152    }
153
154    /// Optional. If node_name is "/Local/Default", this is the path of the database against which
155    /// OD is authenticating.
156    #[inline(always)]
157    pub fn db_path(&self) -> &'a OsStr {
158        // Safety: 'a tied to self, object obtained through ES
159        unsafe { self.raw.db_path.as_os_str() }
160    }
161}
162
163// Safety: safe to send across threads: does not contain any interior mutability nor depend on current thread state
164unsafe impl Send for EventAuthenticationOd<'_> {}
165
166impl_debug_eq_hash_with_functions!(EventAuthenticationOd<'a>; instigator, instigator_token, record_name, node_name, db_path);
167
168/// TouchID authentication data
169#[doc(alias = "es_event_authentication_touchid_t")]
170pub struct EventAuthenticationTouchId<'a> {
171    /// Raw event
172    raw: &'a es_event_authentication_touchid_t,
173    /// Message version
174    version: u32,
175    /// Overall identification success
176    success: bool,
177}
178
179impl<'a> EventAuthenticationTouchId<'a> {
180    /// Process that instigated the authentication (XPC caller that asked for authentication).
181    #[inline(always)]
182    pub fn instigator(&self) -> Option<Process<'a>> {
183        // Safety: 'a tied to self, object obtained through ES
184        let process = unsafe { self.raw.instigator()? };
185        Some(Process::new(process, self.version))
186    }
187
188    /// Audit token of the process that instigated this event.
189    pub fn instigator_token(&self) -> AuditToken {
190        #[cfg(feature = "macos_15_0_0")]
191        if self.version >= 8 {
192            return AuditToken(self.raw.instigator_token);
193        }
194
195        // On old versions, the process was always non-null, and we can get
196        // its token easily.
197        self.instigator().unwrap().audit_token()
198    }
199
200    /// TouchID authentication type
201    #[inline(always)]
202    pub fn touchid_mode(&self) -> es_touchid_mode_t {
203        self.raw.touchid_mode
204    }
205
206    /// Describes whether or not the uid of the user authenticated is available
207    #[inline(always)]
208    pub fn has_uid(&self) -> bool {
209        self.raw.has_uid
210    }
211
212    /// UID of user that was authenticated.
213    #[inline(always)]
214    pub fn uid(&self) -> Option<uid_t> {
215        match (self.has_uid(), self.success, self.touchid_mode()) {
216            // Safety: access is gated on documented conditions
217            (true, true, es_touchid_mode_t::ES_TOUCHID_MODE_VERIFICATION) => unsafe { Some(self.raw.anon0.uid) },
218            _ => None,
219        }
220    }
221}
222
223// Safety: safe to send across threads: does not contain any interior mutability nor depend on current thread state
224unsafe impl Send for EventAuthenticationTouchId<'_> {}
225
226impl_debug_eq_hash_with_functions!(EventAuthenticationTouchId<'a>; instigator, instigator_token, touchid_mode, has_uid, uid);
227
228/// Token authentication data
229#[doc(alias = "es_event_authentication_token_t")]
230pub struct EventAuthenticationToken<'a> {
231    /// Raw event
232    raw: &'a es_event_authentication_token_t,
233    /// Message version
234    version: u32,
235}
236
237impl<'a> EventAuthenticationToken<'a> {
238    /// Process that instigated the authentication (XPC caller that asked for authentication).
239    #[inline(always)]
240    pub fn instigator(&self) -> Option<Process<'a>> {
241        // Safety: 'a tied to self, object obtained through ES
242        let process = unsafe { self.raw.instigator()? };
243        Some(Process::new(process, self.version))
244    }
245
246    /// Audit token of the process that instigated this event.
247    pub fn instigator_token(&self) -> AuditToken {
248        #[cfg(feature = "macos_15_0_0")]
249        if self.version >= 8 {
250            return AuditToken(self.raw.instigator_token);
251        }
252
253        // On old versions, the process was always non-null, and we can get
254        // its token easily.
255        self.instigator().unwrap().audit_token()
256    }
257
258    /// Hash of the public key which CryptoTokenKit is authenticating.
259    #[inline(always)]
260    pub fn pubkey_hash(&self) -> &'a OsStr {
261        // Safety: 'a tied to self, object obtained through ES
262        unsafe { self.raw.pubkey_hash.as_os_str() }
263    }
264
265    /// Token identifier of the event which CryptoTokenKit is authenticating.
266    #[inline(always)]
267    pub fn token_id(&self) -> &'a OsStr {
268        // Safety: 'a tied to self, object obtained through ES
269        unsafe { self.raw.token_id.as_os_str() }
270    }
271
272    /// Optional. This will be available if token is used for GSS PKINIT authentication for
273    /// obtaining a kerberos TGT. `NULL` in all other cases.
274    #[inline(always)]
275    pub fn kerberos_principal(&self) -> &'a OsStr {
276        // Safety: 'a tied to self, object obtained through ES
277        unsafe { self.raw.kerberos_principal.as_os_str() }
278    }
279}
280
281// Safety: safe to send across threads: does not contain any interior mutability nor depend on current thread state
282unsafe impl Send for EventAuthenticationToken<'_> {}
283
284impl_debug_eq_hash_with_functions!(EventAuthenticationToken<'a>; instigator, instigator_token, pubkey_hash, token_id, kerberos_principal);
285
286/// Auto unlock authentication data
287#[doc(alias = "es_event_authentication_auto_unlock_t")]
288pub struct EventAuthenticationAutoUnlock<'a> {
289    /// Raw event
290    raw: &'a es_event_authentication_auto_unlock_t,
291}
292
293impl<'a> EventAuthenticationAutoUnlock<'a> {
294    /// Username for which the authentication was attempted.
295    #[inline(always)]
296    pub fn username(&self) -> &'a OsStr {
297        // Safety: 'a tied to self, object obtained through ES
298        unsafe { self.raw.username.as_os_str() }
299    }
300
301    /// Purpose of the authentication.
302    #[inline(always)]
303    pub fn type_(&self) -> es_auto_unlock_type_t {
304        self.raw.type_
305    }
306}
307
308// Safety: safe to send across threads: does not contain any interior mutability nor depend on current thread state
309unsafe impl Send for EventAuthenticationAutoUnlock<'_> {}
310
311impl_debug_eq_hash_with_functions!(EventAuthenticationAutoUnlock<'a>; username, type_);