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::*;