endpoint_sec_sys/
lib.rs

1//! **Raw** manual bindings for the [Endpoint Security Framework][esf] for Apple targets (macOS)
2//! (referred to as ES in the following documentation).
3//!
4//! Everything that was not present in the original release is feature gated to the macOS version
5//! that saw it released, so you can ensure you don't use any newer functions and types. Additional
6//! checks are done at runtime to return `None` or an `Err` when using something not yet available,
7//! in the [`endpoint-sec`][esc] crate. This crate does not perform the checks since it contains the
8//! raw types and `extern "C"` declaration. This is done because 1) the performance hit of a version
9//! check is negligible in my experience and 2) even if compiled for a newer version where
10//! information `A` is available, your program will still be able to handle older versions since `A`
11//! will be returned in an `Option`.
12//!
13//! ## `Debug` implementations (and `PartialEq`, `Eq`, `Hash`)
14//!
15//! Several types do not have a [`Debug`] implementation because it depends on the [`es_message_t`]
16//! `version` field. In this case, use the `endpoint-sec` crate, which bundle the version with
17//! the data (for example with [`es_event_exec_t`]), allowing to implement `Debug`, [`PartialEq`],
18//! [`Eq`] and [`Hash`] correctly.
19//!
20//! For lots of other types, it's because the implementation would be useless because they contain
21//! pointers like [`es_string_token_t`]: implementing `Debug` for it in a useful way needs `unsafe`
22//! code that we don't want to hide in a `Debug` impl. See the [`endpoint-sec`][esc] crate, with its
23//! higher level types for useful `Debug` impls (and `PartialEq`, `Eq`, `Hash`).
24//!
25#![doc = concat!("[esc]: https://docs.rs/endpoint-sec/", env!("CARGO_PKG_VERSION"), "/endpoint-sec")]
26//! [esf]: https://developer.apple.com/documentation/endpointsecurity
27
28#![cfg(target_os = "macos")]
29#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
30#![allow(
31    deref_nullptr,
32    non_camel_case_types,
33    non_snake_case,
34    clippy::bool_comparison,
35    clippy::missing_safety_doc,
36    clippy::undocumented_unsafe_blocks
37)]
38#![warn(
39    unused_crate_dependencies,
40    unreachable_pub,
41    rustdoc::bare_urls,
42    rustdoc::broken_intra_doc_links
43)]
44
45use core::{fmt, ptr};
46
47/// A wrapper type around `*mut T` to communicate a pointer should not be null without introducing
48/// undefined behaviour.
49///
50/// This is necessary for FFI, where, [`NonNull`][core::ptr::NonNull] asks for much stronger
51/// guarantees, which we can't really provide when getting pointers from behind a C interface.
52/// `ShouldNotBeNull` aims for the same usability as `NonNull` but with panics instead of undefined
53/// behaviour when calling methods like [`as_ref()`][Self::as_ref].
54///
55/// Construction is done either directly from C since `ShouldNotBeNull` is a transparent wrapper or
56/// with [`ShouldNotBeNull::new`].
57#[repr(transparent)]
58pub struct ShouldNotBeNull<T: ?Sized>(*mut T);
59
60impl<T: ?Sized> Clone for ShouldNotBeNull<T> {
61    #[inline]
62    fn clone(&self) -> Self {
63        *self
64    }
65}
66
67impl<T: ?Sized> Copy for ShouldNotBeNull<T> {}
68
69impl<T: ?Sized> ShouldNotBeNull<T> {
70    /// Wraps `p` in a `ShouldNotBeNull`.
71    #[inline]
72    pub fn new(p: *mut T) -> Self {
73        Self(p)
74    }
75
76    /// Calls [`as_ref()`][ptr_ref] on the inner pointer, panicking if it is null.
77    ///
78    /// # Safety
79    ///
80    /// See [`ptr::as_ref()`][ptr_ref] for the exact safety details.
81    ///
82    /// [ptr_ref]: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref-1
83    #[inline]
84    pub unsafe fn as_ref<'a>(&self) -> &'a T {
85        // Safety: see above
86        unsafe { self.0.as_ref().expect("Pointer was null when it should not") }
87    }
88
89    /// Calls [`as_ref()`][ptr_ref] on the inner pointer.
90    ///
91    /// # Safety
92    ///
93    /// See [`ptr::as_ref()`][ptr_ref] for the exact safety details.
94    ///
95    /// [ptr_ref]: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref-1
96    #[inline]
97    pub unsafe fn as_opt<'a>(&self) -> Option<&'a T> {
98        // Safety: see above
99        unsafe { self.0.as_ref() }
100    }
101
102    /// Access to the inner pointer as a `*const`
103    #[inline]
104    pub const fn as_ptr(self) -> *const T {
105        self.0 as *const _
106    }
107}
108
109impl<T: ?Sized> fmt::Debug for ShouldNotBeNull<T> {
110    #[inline]
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        fmt::Pointer::fmt(&self.as_ptr(), f)
113    }
114}
115
116impl<T: ?Sized> fmt::Pointer for ShouldNotBeNull<T> {
117    #[inline]
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        fmt::Pointer::fmt(&self.as_ptr(), f)
120    }
121}
122
123impl<T: ?Sized> Eq for ShouldNotBeNull<T> {}
124
125impl<T: ?Sized> PartialEq for ShouldNotBeNull<T> {
126    #[inline]
127    fn eq(&self, other: &Self) -> bool {
128        ptr::eq(self.as_ptr(), other.as_ptr())
129    }
130}
131
132/// Provides an unsafe wrapper to access some data as a slice in a type
133macro_rules! slice_access {
134    ($ty:ty[. $data:ident; . $count:ident]: fn $fn_name:ident() -> $slice_ty:ty) => {
135        impl $ty {
136            /// Uses `
137            #[doc = ::core::stringify!($count)]
138            /// ` and `
139            #[doc = ::core::stringify!($data)]
140            /// ` to give access to a slice view.
141            ///
142            /// # Safety
143            ///
144            /// The count and data should be in sync. If the count is not 0, the data should be a
145            /// valid (aligned & non-null) pointer to initialized memory.
146            #[inline]
147            pub unsafe fn $fn_name(&self) -> &[$slice_ty] {
148                if self.$count > 0 && self.$data.is_null() == false {
149                    // Safety: `$data` is non-null, aligned (except if Apple mucked things up or the caller
150                    // constructed an invalid value), `$data` is non-zero
151                    unsafe { ::core::slice::from_raw_parts(self.$data, self.$count) }
152                } else {
153                    &[]
154                }
155            }
156        }
157    };
158}
159
160/// C enums cannot be directly represented as Rust enums, instead they have to be declared as
161/// structs wrapping the correct integer (often an `u32`).
162///
163/// This macro will also ensure the `Debug` implemenation is nice to look at by showing the variant
164/// instead of just the numeric value when it is known.
165///
166/// # Usage
167///
168/// In order for `== LAST` to work, variants must be defined in increasing numeric order. Failure to
169/// do so will result in the `LAST` variant containing the wrong value.
170macro_rules! ffi_wrap_enum {
171    (
172        $(#[$doc_enum:meta])*
173        $enum_name: ident ($inner_type: ty);
174        $(
175            == LAST;
176            $(#[$doc_last:meta])*
177            $variant_last: ident,
178        )?
179
180        $(
181            == MACOS_10_15_0;
182            $(
183                $(#[$doc_10_15_0:meta])*
184                $variant_10_15_0: ident = $value_10_15_0: literal,
185            )*
186
187            --
188
189            $(#[$doc_last_10_15_0:meta])*
190            $variant_last_10_15_0: ident = $value_last_10_15_0: literal,
191        )?
192        $(
193            == MACOS_10_15_1;
194            $(
195                $(#[$doc_10_15_1:meta])*
196                $variant_10_15_1: ident = $value_10_15_1: literal,
197            )*
198
199            --
200
201            $(#[$doc_last_10_15_1:meta])*
202            $variant_last_10_15_1: ident = $value_last_10_15_1: literal,
203        )?
204        $(
205            == MACOS_10_15_4;
206            $(
207                $(#[$doc_10_15_4:meta])*
208                $variant_10_15_4: ident = $value_10_15_4: literal,
209            )*
210
211            --
212
213            $(#[$doc_last_10_15_4:meta])*
214            $variant_last_10_15_4: ident = $value_last_10_15_4: literal,
215        )?
216        $(
217            == MACOS_11_0_0;
218            $(
219                $(#[$doc_11_0_0:meta])*
220                $variant_11_0_0: ident = $value_11_0_0: literal,
221            )*
222
223            --
224
225            $(#[$doc_last_11_0_0:meta])*
226            $variant_last_11_0_0: ident = $value_last_11_0_0: literal,
227        )?
228        $(
229            == MACOS_11_3_0;
230            $(
231                $(#[$doc_11_3_0:meta])*
232                $variant_11_3_0: ident = $value_11_3_0: literal,
233            )*
234
235            --
236
237            $(#[$doc_last_11_3_0:meta])*
238            $variant_last_11_3_0: ident = $value_last_11_3_0: literal,
239        )?
240        $(
241            == MACOS_12_0_0;
242            $(
243                $(#[$doc_12_0_0:meta])*
244                $variant_12_0_0: ident = $value_12_0_0: literal,
245            )*
246
247            --
248
249            $(#[$doc_last_12_0_0:meta])*
250            $variant_last_12_0_0: ident = $value_last_12_0_0: literal,
251        )?
252        $(
253            == MACOS_13_0_0;
254            $(
255                $(#[$doc_13_0_0:meta])*
256                $variant_13_0_0: ident = $value_13_0_0: literal,
257            )*
258
259            --
260
261            $(#[$doc_last_13_0_0:meta])*
262            $variant_last_13_0_0: ident = $value_last_13_0_0: literal,
263        )?
264        $(
265            == MACOS_14_0_0;
266            $(
267                $(#[$doc_14_0_0:meta])*
268                $variant_14_0_0: ident = $value_14_0_0: literal,
269            )*
270
271            --
272
273            $(#[$doc_last_14_0_0:meta])*
274            $variant_last_14_0_0: ident = $value_last_14_0_0: literal,
275        )?
276    ) => {
277        $(#[$doc_enum])*
278        #[repr(transparent)]
279        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
280        pub struct $enum_name(pub $inner_type);
281
282        $(
283            /// Variants available from macOS 10.15.0 onwards
284            impl $enum_name {
285                $(
286                    $(#[$doc_10_15_0])*
287                    pub const $variant_10_15_0: $enum_name = $enum_name($value_10_15_0);
288                )*
289
290                $(#[$doc_last_10_15_0])*
291                ///
292                /// Last value for macOS 10.15.0
293                pub const $variant_last_10_15_0: $enum_name = $enum_name($value_last_10_15_0);
294
295                /// Easily identifiable name for the last member of macOS 10.15.0
296                pub const LAST_10_15_0: $enum_name = $enum_name::$variant_last_10_15_0;
297            }
298        )?
299        $(
300            /// Variants available from macOS 10.15.1 onwards
301            #[cfg(feature = "macos_10_15_1")]
302            impl $enum_name {
303                $(
304                    $(#[$doc_10_15_1])*
305                    pub const $variant_10_15_1: $enum_name = $enum_name($value_10_15_1);
306                )*
307
308                $(#[$doc_last_10_15_1])*
309                ///
310                /// Last value for macOS 10.15.1
311                pub const $variant_last_10_15_1: $enum_name = $enum_name($value_last_10_15_1);
312
313                /// Easily identifiable name for the last member of macOS 10.15.1
314                pub const LAST_10_15_1: $enum_name = $enum_name::$variant_last_10_15_1;
315            }
316        )?
317        $(
318            /// Variants available from macOS 10.15.4 onwards
319            #[cfg(feature = "macos_10_15_4")]
320            impl $enum_name {
321                $(
322                    $(#[$doc_10_15_4])*
323                    pub const $variant_10_15_4: $enum_name = $enum_name($value_10_15_4);
324                )*
325
326                $(#[$doc_last_10_15_4])*
327                ///
328                /// Last value for macOS 10.15.4
329                pub const $variant_last_10_15_4: $enum_name = $enum_name($value_last_10_15_4);
330
331                /// Easily identifiable name for the last member of macOS 10.15.4
332                pub const LAST_10_15_4: $enum_name = $enum_name::$variant_last_10_15_4;
333            }
334        )?
335        $(
336            /// Variants available from macOS 11.0.0 onwards
337            #[cfg(feature = "macos_11_0_0")]
338            impl $enum_name {
339                $(
340                    $(#[$doc_11_0_0])*
341                    pub const $variant_11_0_0: $enum_name = $enum_name($value_11_0_0);
342                )*
343
344                $(#[$doc_last_11_0_0])*
345                ///
346                /// Last value for macOS 11.0.0
347                pub const $variant_last_11_0_0: $enum_name = $enum_name($value_last_11_0_0);
348
349                /// Easily identifiable name for the last member of macOS 11.0.0
350                pub const LAST_11_0_0: $enum_name = $enum_name::$variant_last_11_0_0;
351            }
352        )?
353        $(
354            /// Variants available from macOS 11.3.0 onwards
355            #[cfg(feature = "macos_11_3_0")]
356            impl $enum_name {
357                $(
358                    $(#[$doc_11_3_0])*
359                    pub const $variant_11_3_0: $enum_name = $enum_name($value_11_3_0);
360                )*
361
362                $(#[$doc_last_11_3_0])*
363                ///
364                /// Last value for macOS 11.3.0
365                pub const $variant_last_11_3_0: $enum_name = $enum_name($value_last_11_3_0);
366
367                /// Easily identifiable name for the last member of macOS 11.3.0
368                pub const LAST_11_3_0: $enum_name = $enum_name::$variant_last_11_3_0;
369            }
370        )?
371        $(
372            /// Variants available from macOS 12.0.0 onwards
373            #[cfg(feature = "macos_12_0_0")]
374            impl $enum_name {
375                $(
376                    $(#[$doc_12_0_0])*
377                    pub const $variant_12_0_0: $enum_name = $enum_name($value_12_0_0);
378                )*
379
380                $(#[$doc_last_12_0_0])*
381                ///
382                /// Last value for macOS 12.0.0
383                pub const $variant_last_12_0_0: $enum_name = $enum_name($value_last_12_0_0);
384
385                /// Easily identifiable name for the last member of macOS 12.0.0
386                pub const LAST_12_0_0: $enum_name = $enum_name::$variant_last_12_0_0;
387            }
388        )?
389        $(
390            /// Variants available from macOS 13.0.0 onwards
391            #[cfg(feature = "macos_13_0_0")]
392            impl $enum_name {
393                $(
394                    $(#[$doc_13_0_0])*
395                    pub const $variant_13_0_0: $enum_name = $enum_name($value_13_0_0);
396                )*
397
398                $(#[$doc_last_13_0_0])*
399                ///
400                /// Last value for macOS 13.0.0
401                pub const $variant_last_13_0_0: $enum_name = $enum_name($value_last_13_0_0);
402
403                /// Easily identifiable name for the last member of macOS 13.0.0
404                pub const LAST_13_0_0: $enum_name = $enum_name::$variant_last_13_0_0;
405            }
406        )?
407        $(
408            /// Variants available from macOS 14.0.0 onwards
409            #[cfg(feature = "macos_14_0_0")]
410            impl $enum_name {
411                $(
412                    $(#[$doc_14_0_0])*
413                    pub const $variant_14_0_0: $enum_name = $enum_name($value_14_0_0);
414                )*
415
416                $(#[$doc_last_14_0_0])*
417                ///
418                /// Last value for macOS 14.0.0
419                pub const $variant_last_14_0_0: $enum_name = $enum_name($value_last_14_0_0);
420
421                /// Easily identifiable name for the last member of macOS 14.0.0
422                pub const LAST_14_0_0: $enum_name = $enum_name::$variant_last_14_0_0;
423            }
424        )?
425
426        impl $enum_name {
427            const __COMPUTED_LAST_VARIANT: $enum_name = $enum_name({
428                const LAST_VALUE: $enum_name = match &[
429                    $(#[cfg(feature = "macos_14_0_0")] $enum_name::$variant_last_14_0_0,)?
430                    $(#[cfg(feature = "macos_13_0_0")] $enum_name::$variant_last_13_0_0,)?
431                    $(#[cfg(feature = "macos_12_0_0")] $enum_name::$variant_last_12_0_0,)?
432                    $(#[cfg(feature = "macos_11_3_0")] $enum_name::$variant_last_11_3_0,)?
433                    $(#[cfg(feature = "macos_11_0_0")] $enum_name::$variant_last_11_0_0,)?
434                    $(#[cfg(feature = "macos_10_15_4")] $enum_name::$variant_last_10_15_4,)?
435                    $(#[cfg(feature = "macos_10_15_1")] $enum_name::$variant_last_10_15_1,)?
436                    $($enum_name::$variant_last_10_15_0,)?
437                ] {
438                    [first, ..] => *first,
439                };
440
441                LAST_VALUE.0 + 1
442            });
443
444            $(
445                /// Not a real instance but a convenience value for operating on the range of defined
446                /// variants This was available starting in macos 10.15.0.
447                ///
448                /// It can be used to ensure that, even on newer versions of Endpoint Security, you do not
449                /// crash on unknown variants: if the variant value is superior or equal to this value (set
450                /// at compile time), you can ignore it.
451                ///
452                /// See an example with [`es_event_type_t::ES_EVENT_TYPE_LAST`].
453                pub const $variant_last: $enum_name = $enum_name::__COMPUTED_LAST_VARIANT;
454            )?
455        }
456
457        impl ::core::fmt::Debug for $enum_name {
458            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
459                match *self {
460                    $($(
461                        Self::$variant_10_15_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_10_15_0($value_10_15_0)),
462                    )*)?
463                    $(
464                        Self::$variant_last_10_15_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_last_10_15_0($value_last_10_15_0)),
465                    )?
466                    $($(
467                        #[cfg(feature = "macos_10_15_1")]
468                        Self::$variant_10_15_1 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_10_15_1($value_10_15_1)),
469                    )*)?
470                    $(
471                        #[cfg(feature = "macos_10_15_1")]
472                        Self::$variant_last_10_15_1 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_last_10_15_1($value_last_10_15_1)),
473                    )?
474                    $($(
475                        #[cfg(feature = "macos_10_15_4")]
476                        Self::$variant_10_15_4 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_10_15_4($value_10_15_4)),
477                    )*)?
478                    $(
479                        #[cfg(feature = "macos_10_15_4")]
480                        Self::$variant_last_10_15_4 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_last_10_15_4($value_last_10_15_4)),
481                    )?
482                    $($(
483                        #[cfg(feature = "macos_11_0_0")]
484                        Self::$variant_11_0_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_11_0_0($value_11_0_0)),
485                    )*)?
486                    $(
487                        #[cfg(feature = "macos_11_0_0")]
488                        Self::$variant_last_11_0_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_last_11_0_0($value_last_11_0_0)),
489                    )?
490                    $($(
491                        #[cfg(feature = "macos_11_3_0")]
492                        Self::$variant_11_3_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_11_3_0($value_11_3_0)),
493                    )*)?
494                    $(
495                        #[cfg(feature = "macos_11_3_0")]
496                        Self::$variant_last_11_3_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_last_11_3_0($value_last_11_3_0)),
497                    )?
498                    $($(
499                        #[cfg(feature = "macos_12_0_0")]
500                        Self::$variant_12_0_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_12_0_0($value_12_0_0)),
501                    )*)?
502                    $(
503                        #[cfg(feature = "macos_12_0_0")]
504                        Self::$variant_last_12_0_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_last_12_0_0($value_last_12_0_0)),
505                    )?
506                    $($(
507                        #[cfg(feature = "macos_13_0_0")]
508                        Self::$variant_13_0_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_13_0_0($value_13_0_0)),
509                    )*)?
510                    $(
511                        #[cfg(feature = "macos_13_0_0")]
512                        Self::$variant_last_13_0_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_last_13_0_0($value_last_13_0_0)),
513                    )?
514                    $(
515                        #[cfg(feature = "macos_14_0_0")]
516                        Self::$variant_last_14_0_0 => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_last_14_0_0($value_last_14_0_0)),
517                    )?
518                    $(
519                        Self::$variant_last => ::core::write!(
520                            f, ::core::concat!(::core::stringify!($enum_name), "::", ::core::stringify!($variant_last), "({})"), self.0
521                        ),
522                    )?
523                    unknown => ::core::write!(f, ::core::concat!(::core::stringify!($enum_name), "({})"), unknown.0),
524                }
525            }
526        }
527    };
528    (DEBUG $f: ident, $enum_name: ident :: $variant_name: ident ($variant_value: literal)) => {
529        ::core::write!($f, ::core::concat!(
530            ::core::stringify!($enum_name), "::", ::core::stringify!($variant_name), "(", $variant_value, ")",
531        ))
532    }
533}
534
535/// Provides an access function for fields that are `ShouldNotBeNull<T>`
536macro_rules! should_not_be_null_fields {
537    ($ty: ty; $($field: ident -> $field_ty: ty),+ $(,)?) => {
538        /// Accessors for `ShouldNotBeNull` fields
539        impl $ty {
540            $(
541                /// Gives a references to the field while checking for null.
542                ///
543                /// # Safety
544                ///
545                /// See [`ShouldNotBeNull`][crate::ShouldNotBeNull] safety requirements.
546                #[inline]
547                pub unsafe fn $field(&self) -> &$field_ty {
548                    // Safety: see above
549                    unsafe { $crate::ShouldNotBeNull::as_ref(&self.$field) }
550                }
551            )+
552        }
553    };
554}
555
556/// Provides an access function for fields that are `*mut T` or `*const T`
557#[cfg(feature = "macos_10_15_1")]
558macro_rules! null_fields {
559    ($ty: ty; $($field: ident -> $field_ty: ty),+ $(,)?) => {
560        /// Accessors for `*mut` and `*const` fields
561        impl $ty {
562            $(
563                /// Helper to avoid the `is_null()` + deref every time.
564                ///
565                /// # Safety
566                ///
567                /// The pointer must be valid (aligned & initialized) for a value of the expected
568                /// type.
569                #[inline]
570                pub unsafe fn $field(&self) -> Option<&$field_ty> {
571                    // Safety: see above
572                    unsafe { self.$field.as_ref() }
573                }
574            )+
575        }
576    };
577}
578
579mod types;
580pub use types::*;
581
582mod message;
583pub use message::*;
584
585mod client;
586pub use client::*;
587
588mod additional;
589pub use additional::*;
590
591// Helper due to Rust's orphan rule
592mod result_wrapping;
593pub use result_wrapping::*;