Skip to main content

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))]
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_first:meta])*
184                $variant_first: ident = $value_first: literal,
185            )*
186
187            --
188
189            $(#[$doc_last_first:meta])*
190            $variant_last_first: ident = $value_last_first: literal,
191        )?
192        $(
193            == #[cfg(feature = $ver_feature_lit:literal)] $ver_ident:tt $ver_str:literal;
194            $(
195                $(#[$doc_ver:meta])*
196                $variant_ver: ident = $value_ver: literal,
197            )*
198
199            --
200
201            $(#[$doc_last_ver:meta])*
202            $variant_last_ver: ident = $value_last_ver: literal,
203        )*
204    ) => {
205        $(#[$doc_enum])*
206        #[repr(transparent)]
207        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
208        pub struct $enum_name(pub $inner_type);
209
210        $(
211            /// Variants available from macOS 10.15.0 onwards
212            impl $enum_name {
213                $(
214                    $(#[$doc_first])*
215                    pub const $variant_first: $enum_name = $enum_name($value_first);
216                )*
217
218                $(#[$doc_last_first])*
219                ///
220                /// Last value for macOS 10.15.0
221                pub const $variant_last_first: $enum_name = $enum_name($value_last_first);
222            }
223        )?
224        $(
225            #[doc = "Variants available from macOS "]
226            #[doc = $ver_str]
227            #[doc = " onwards"]
228            #[cfg(feature = $ver_feature_lit)]
229            impl $enum_name {
230                $(
231                    $(#[$doc_ver])*
232                    pub const $variant_ver: $enum_name = $enum_name($value_ver);
233                )*
234
235                $(#[$doc_last_ver])*
236                ///
237                #[doc = "Last value for macOS " ]
238                #[doc = $ver_str]
239                pub const $variant_last_ver: $enum_name = $enum_name($value_last_ver);
240            }
241        )*
242
243        impl $enum_name {
244            const __COMPUTED_LAST_VARIANT: $enum_name = $enum_name({
245                const LAST_VALUE: $enum_name = match &[
246                    $($enum_name::$variant_last_first,)?
247                    $(#[cfg(feature = $ver_feature_lit)] $enum_name::$variant_last_ver,)?
248                ] {
249                    [.., last] => *last,
250                };
251
252                LAST_VALUE.0 + 1
253            });
254
255            $(
256                /// Not a real instance but a convenience value for operating on the range of defined
257                /// variants.
258                ///
259                /// It can be used to ensure that, even on newer versions of Endpoint Security, you do not
260                /// crash on unknown variants: if the variant value is superior or equal to this value (set
261                /// at compile time), you can ignore it.
262                ///
263                /// See an example with [`es_event_type_t::ES_EVENT_TYPE_LAST`].
264                pub const $variant_last: $enum_name = $enum_name::__COMPUTED_LAST_VARIANT;
265            )?
266        }
267
268        impl ::core::fmt::Debug for $enum_name {
269            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
270                match *self {
271                    $($(
272                        Self::$variant_first => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_first($value_first)),
273                    )*)?
274                    $(
275                        Self::$variant_last_first => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_last_first($value_last_first)),
276                    )?
277                    $(
278                        $(
279                            #[cfg(feature = $ver_feature_lit)]
280                            Self::$variant_ver => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_ver($value_ver)),
281                        )*
282                        #[cfg(feature = $ver_feature_lit)]
283                        Self::$variant_last_ver => ffi_wrap_enum!(DEBUG f, $enum_name::$variant_last_ver($value_last_ver)),
284                    )*
285                    $(
286                        Self::$variant_last => ::core::write!(
287                            f, ::core::concat!(::core::stringify!($enum_name), "::", ::core::stringify!($variant_last), "({})"), self.0
288                        ),
289                    )?
290                    unknown => ::core::write!(f, ::core::concat!(::core::stringify!($enum_name), "({})"), unknown.0),
291                }
292            }
293        }
294    };
295    (DEBUG $f: ident, $enum_name: ident :: $variant_name: ident ($variant_value: literal)) => {
296        ::core::write!($f, ::core::concat!(
297            ::core::stringify!($enum_name), "::", ::core::stringify!($variant_name), "(", $variant_value, ")",
298        ))
299    }
300}
301
302/// Provides an access function for fields that are `ShouldNotBeNull<T>`
303macro_rules! should_not_be_null_fields {
304    ($ty: ty; $($field: ident -> $field_ty: ty),+ $(,)?) => {
305        /// Accessors for `ShouldNotBeNull` fields
306        impl $ty {
307            $(
308                /// Gives a references to the field while checking for null.
309                ///
310                /// # Safety
311                ///
312                /// See [`ShouldNotBeNull`][crate::ShouldNotBeNull] safety requirements.
313                #[inline]
314                pub unsafe fn $field(&self) -> &$field_ty {
315                    // Safety: see above
316                    unsafe { $crate::ShouldNotBeNull::as_ref(&self.$field) }
317                }
318            )+
319        }
320    };
321}
322
323/// Provides an access function for fields that are `*mut T` or `*const T`
324macro_rules! null_fields {
325    ($ty: ty; $($field: ident -> $field_ty: ty),+ $(,)?) => {
326        /// Accessors for `*mut` and `*const` fields
327        impl $ty {
328            $(
329                /// Helper to avoid the `is_null()` + deref every time.
330                ///
331                /// # Safety
332                ///
333                /// The pointer must be valid (aligned & initialized) for a value of the expected
334                /// type.
335                #[inline]
336                pub unsafe fn $field(&self) -> Option<&$field_ty> {
337                    // Safety: see above
338                    unsafe { self.$field.as_ref() }
339                }
340            )+
341        }
342    };
343}
344
345mod types;
346pub use types::*;
347
348mod message;
349pub use message::*;
350
351mod client;
352pub use client::*;
353
354mod additional;
355pub use additional::*;
356
357// Helper due to Rust's orphan rule
358mod result_wrapping;
359pub use result_wrapping::*;