endpoint_sec_sys/
client.rs

1//! Corresponding header: `EndpointSecurity/ESClient.h`
2
3// Types and methods should be added in the same order as they are in the original header to make
4// maintenance easier.
5
6use core::marker::PhantomData;
7use core::ptr::NonNull;
8
9pub use block2;
10use block2::Block;
11use libc::c_char;
12pub use libc::c_void;
13use objc2::{Encoding, RefEncode};
14
15use super::{
16    audit_token_t, es_auth_result_t, es_clear_cache_result_t, es_event_type_t, es_message_t, es_new_client_result_t,
17    es_respond_result_t, es_return_t,
18};
19#[cfg(feature = "macos_13_0_0")]
20use super::{es_mute_inversion_type_t, es_mute_inverted_return_t};
21#[cfg(feature = "macos_12_0_0")]
22use super::{es_mute_path_type_t, es_muted_paths_t, es_muted_processes_t};
23
24/// Opaque type that stores the endpoint security client state.
25///
26/// Neither [`Send`] nor [`Sync`].
27#[repr(transparent)]
28pub struct es_client_t(u8, PhantomData<*mut u8>);
29
30unsafe impl RefEncode for es_client_t {
31    const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Unknown);
32}
33
34#[link(name = "EndpointSecurity", kind = "dylib")]
35extern "C" {
36    /// Subscribe to some set of events
37    ///
38    /// - `client`: The client that will be subscribing
39    /// - `events`: Array of es_event_type_t to subscribe to
40    /// - `event_count`: Count of es_event_type_t in `events`
41    ///
42    /// Subscribing to new event types does not remove previous subscriptions.
43    ///
44    /// Subscribing to events is not optional for clients that have opted into early boot mode (see
45    /// `NSEndpointSecurityEarlyBoot` in `EndpointSecurity(7)`). Early boot clients that fail to
46    /// subscribe to at least one event type will cause early boot to time out, resulting in a bad
47    /// user experience and risking watchdog timeout panics.
48    pub fn es_subscribe(client: *mut es_client_t, events: *const es_event_type_t, event_count: u32) -> es_return_t;
49
50    /// Unsubscribe from some set of events
51    ///
52    /// - `client`: The client that will be unsubscribing
53    /// - `events`: Array of es_event_type_t to unsubscribe from
54    /// - `event_count`: Count of es_event_type_t in `events`
55    ///
56    /// Events not included in the given `events` array that were previously subscribed to will
57    /// continue to be subscribed to.
58    pub fn es_unsubscribe(client: *mut es_client_t, events: *const es_event_type_t, event_count: u32) -> es_return_t;
59
60    /// Unsubscribe from all events
61    ///
62    /// - `client`: The client that will be unsubscribing
63    pub fn es_unsubscribe_all(client: *mut es_client_t) -> es_return_t;
64
65    /// List subscriptions
66    ///
67    /// - `client`: The client for which subscriptions will be listed
68    /// - `count`: Out param that reports the number of subscriptions written
69    /// - `subscriptions`:  Out param for pointer to subscription data
70    ///
71    /// The caller takes ownership of the memory at `*subscriptions` and must free it.
72    pub fn es_subscriptions(
73        client: *mut es_client_t,
74        count: *mut usize,
75        subscriptions: *mut *mut es_event_type_t,
76    ) -> es_return_t;
77
78    /// Respond to an auth event that requires an [`es_auth_result_t`] response
79    ///
80    /// - `client`: The client that produced the event
81    /// - `message`: The message being responded to
82    /// - `result`: A result indicating the action the ES subsystem should take
83    /// - `cache`: Indicates if this result should be cached. The specific caching semantics depend
84    ///   on [`es_event_type_t`].
85    ///   Cache key is generally the involved files, with modifications to those files
86    ///   invalidating the cache entry. A cache hit leads to no AUTH event being produced,
87    ///   while still producing a NOTIFY event normally. The cache argument is ignored for
88    ///   events that do not support caching.
89    ///
90    /// Some events must be responded to with [`es_respond_flags_result()`]. Responding to flags
91    /// events with this function will fail.
92    pub fn es_respond_auth_result(
93        client: *mut es_client_t,
94        message: *const es_message_t,
95        result: es_auth_result_t,
96        cache: bool,
97    ) -> es_respond_result_t;
98
99    /// Respond to an auth event that requires an `u32` flags response
100    ///
101    /// - `client`: The client that produced the event
102    /// - `message`: The message being responded to
103    /// - `authorized_flags`: A flags value that will mask the flags in event being responded to;
104    ///   pass 0 to deny and `u32::MAX` to allow regardless of what flags are
105    ///   set on the event.
106    /// - `cache`: Indicates if this result should be cached. The specific caching semantics depend
107    ///   on [`es_event_type_t`].
108    ///   Cache key is generally the involved files, with modifications to those files
109    ///   invalidating the cache entry. A cache hit leads to no AUTH event being produced,
110    ///   while still producing a NOTIFY event normally. The cache argument is ignored for
111    ///   events that do not support caching.
112    ///
113    /// Some events must be responded to with [`es_respond_auth_result()`]. Responding to auth
114    /// events with the function will fail.
115    ///
116    /// Enabling caching caches `authorized_flags`. Subsequent cache hits will result in the
117    /// event being allowed only if the flags of the event are a subset of the flags in
118    /// `authorized_flags`, and denied otherwise. As a result, `u32::MAX` should be passed
119    /// for `authorized_flags`, unless denying events with certain flags is intentional.
120    /// A common mistake is passing the flags from the event, which together with caching
121    /// may result in subsequent events getting unintentionally denied if they have flags
122    /// set that were not set in the cached `authorized_flags`.
123    pub fn es_respond_flags_result(
124        client: *mut es_client_t,
125        message: *const es_message_t,
126        authorized_flags: u32,
127        cache: bool,
128    ) -> es_respond_result_t;
129
130    /// Suppress all events from the process described by the given `audit_token`
131    ///
132    /// - `client`: The client for which events will be suppressed
133    /// - `audit_token`: The audit token of the process for which events will be suppressed
134    ///
135    #[cfg_attr(feature = "macos_12_0_0", doc = "See [`es_mute_process_events()`]")]
136    #[cfg_attr(not(feature = "macos_12_0_0"), doc = "See `es_mute_process_events()`")]
137    pub fn es_mute_process(client: *mut es_client_t, audit_token: *const audit_token_t) -> es_return_t;
138
139    /// Suppress a subset of events from the process described by the given `audit_token`
140    ///
141    /// - `client`: The client for which events will be suppressed
142    /// - `audit_token`: The audit token of the process for which events will be suppressed
143    /// - `events`: Array of event types for which the audit_token should be muted.
144    /// - `event_count`: The number of items in the `events` array.
145    ///
146    /// See [`es_mute_process()`]
147    #[cfg(feature = "macos_12_0_0")]
148    pub fn es_mute_process_events(
149        client: *mut es_client_t,
150        audit_token: *const audit_token_t,
151        events: *const es_event_type_t,
152        event_count: usize,
153    ) -> es_return_t;
154
155    /// Unmute a process for all event types
156    ///
157    /// - `client`: The client for which the process will be unmuted
158    /// - `audit_token`: The audit token of the process to be unmuted
159    ///
160    #[cfg_attr(feature = "macos_12_0_0", doc = "See [`es_unmute_process_events()`]")]
161    #[cfg_attr(not(feature = "macos_12_0_0"), doc = "See `es_unmute_process_events()`")]
162    pub fn es_unmute_process(client: *mut es_client_t, audit_token: *const audit_token_t) -> es_return_t;
163
164    /// Unmute a process for a subset of event types.
165    ///
166    /// - `client`: The client for which events will be unmuted
167    /// - `audit_token`: The audit token of the process for which events will be unmuted
168    /// - `events`: Array of event types to unmute for the process
169    /// - `event_count`: The number of items in the `events` array.
170    ///
171    /// See [`es_unmute_path()`]
172    #[cfg(feature = "macos_12_0_0")]
173    pub fn es_unmute_process_events(
174        client: *mut es_client_t,
175        audit_token: *const audit_token_t,
176        events: *const es_event_type_t,
177        event_count: usize,
178    ) -> es_return_t;
179
180    /// List muted processes
181    ///
182    /// - `client`: The client for which muted processes will be listed
183    /// - `count`: Out param that reports the number of audit tokens written
184    /// - `audit_tokens`:  Out param for pointer to audit_token data
185    ///
186    /// The caller takes ownership of the memory at `*audit_tokens` and must free it. If there
187    /// are no muted processes and the call completes successfully, `*count` is set to 0 and
188    /// `*audit_token` is set to `NULL`.
189    ///
190    /// The audit tokens are returned in the same state as they were passed to [`es_mute_process()`]
191    /// and may not accurately reflect the current state of the respective processes.
192    pub fn es_muted_processes(
193        client: *mut es_client_t,
194        count: *mut usize,
195        audit_tokens: *mut *mut audit_token_t,
196    ) -> es_return_t;
197
198    /// Retrieve a list of all muted processes.
199    ///
200    /// - `client`: The es_client_t for which the muted processes will be retrieved.
201    /// - `muted_processes`: OUT param the will contain newly created memory describing the set of
202    ///   muted processes. This memory must be deleted using [`es_release_muted_processes`].
203    ///
204    /// See [`es_release_muted_processes()`]
205    #[cfg(feature = "macos_12_0_0")]
206    pub fn es_muted_processes_events(
207        client: *mut es_client_t,
208        muted_processes: *mut *mut es_muted_processes_t,
209    ) -> es_return_t;
210
211    /// Delete a set of muted processes obtained from `es_muted_processes_events`, freeing resources.
212    ///
213    /// - `muted_processes`: A set of muted processes to delete.
214    ///
215    /// See [`es_muted_processes_events()`]
216    #[cfg(feature = "macos_12_0_0")]
217    pub fn es_release_muted_processes(muted_processes: *mut es_muted_processes_t);
218
219    /// Suppress all events matching a path.
220    ///
221    /// - `client`: The es_client_t for which the path will be muted.
222    /// - `path`: The path to mute.
223    /// - `type`: Describes the type of the `path` parameter.
224    ///
225    /// Path-based muting applies to the real and potentially firmlinked path of a file as seen by
226    /// VFS, and as available from `fcntl(2)` `F_GETPATH`. No special provisions are made for files
227    /// with multiple ("hard") links, or for symbolic links.
228    ///
229    /// In particular, when using inverted target path muting to monitor a particular path for
230    /// writing, you will need to check if the file(s) of interest are also reachable via additional
231    /// hard links outside of the paths you are observing.
232    ///
233    /// See [`es_mute_path_events()`]
234    ///
235    /// When using the path types `ES_MUTE_PATH_TYPE_TARGET_PREFIX` and
236    /// `ES_MUTE_PATH_TYPE_TARGET_LITERAL` not all events are supported. Furthermore the
237    /// interpretation of target path is contextual. For events with more than one target path (such
238    /// as [`es_event_exchangedata_t`][crate::es_event_exchangedata_t]) the behavior depends on the
239    /// mute inversion state:
240    ///
241    /// - Under normal muting the event is suppressed only if **ALL** paths are muted
242    /// - When target path muting is inverted the event is selected if **ANY** target path is muted
243    ///
244    /// For example a rename will be suppressed if and only if both the source path and destination
245    /// path are muted. Supported events are listed below. For each event the target path is defined
246    /// as:
247    ///
248    /// - [`EXEC`][crate::es_event_exec_t]:
249    ///   The file being executed
250    /// - [`OPEN`][crate::es_event_open_t]:
251    ///   The file being opened
252    /// - [`MMAP`][crate::es_event_mmap_t]:
253    ///   The file being memory mapped
254    /// - [`RENAME`][crate::es_event_rename_t]:
255    ///   Both the source and destination path.
256    /// - [`SIGNAL`][crate::es_event_signal_t]:
257    ///   The path of the process being signalled
258    /// - [`UNLINK`][crate::es_event_unlink_t]:
259    ///   The file being unlinked
260    /// - [`CLOSE`][crate::es_event_close_t]:
261    ///   The file being closed
262    /// - [`CREATE`][crate::es_event_create_t]:
263    ///   The path to the file that will be created or replaced
264    /// - [`GET_TASK`][crate::es_event_get_task_t]:
265    ///   The path of the process for which the task port
266    ///   is being retrieved
267    /// - [`LINK`][crate::es_event_link_t]:
268    ///   Both the source and destination path
269    /// - [`SETATTRLIST`][crate::es_event_setattrlist_t]:
270    ///   The file for which the attributes are being set
271    /// - [`SETEXTATTR`][crate::es_event_setextattr_t]:
272    ///   The file for which the extended attributes are being set
273    /// - [`SETFLAGS`][crate::es_event_setflags_t]:
274    ///   The file for which flags are being set
275    /// - [`SETMODE`][crate::es_event_setmode_t]:
276    ///   The file for which the mode is being set
277    /// - [`SETOWNER`][crate::es_event_setowner_t]:
278    ///   The file for which the owner is being set
279    /// - [`WRITE`][crate::es_event_write_t]:
280    ///   The file being written to
281    /// - [`READLINK`][crate::es_event_readlink_t]:
282    ///   The symbolic link being resolved
283    /// - [`TRUNCATE`][crate::es_event_truncate_t]:
284    ///   The file being truncated
285    /// - [`CHDIR`][crate::es_event_chdir_t]:
286    ///   The new working directory
287    /// - [`GETATTRLIST`][crate::es_event_getattrlist_t]:
288    ///   The file for which the attribute list is being retrieved
289    /// - [`STAT`][crate::es_event_stat_t]:
290    ///   The file for which the stat is being retrieved
291    /// - [`ACCESS`][crate::es_event_access_t]:
292    ///   The file for which access is being tested
293    /// - [`CHROOT`][crate::es_event_chroot_t]:
294    ///   The file which will become the new root
295    /// - [`UTIMES`][crate::es_event_utimes_t]:
296    ///   The file for which times are being set
297    /// - [`CLONE`][crate::es_event_clone_t]:
298    ///   Both the source file and target path
299    /// - [`FCNTL`][crate::es_event_fcntl_t]:
300    ///   The file under file control
301    /// - [`GETEXTATTR`][crate::es_event_getextattr_t]:
302    ///   The file for which extended attributes are being retrieved
303    /// - [`LISTEXTATTR`][crate::es_event_listextattr_t]:
304    ///   The file for which extended attributes are being listed
305    /// - [`READDIR`][crate::es_event_readdir_t]:
306    ///   The directory for whose contents will be read
307    /// - [`DELETEEXTATTR`][crate::es_event_deleteextattr_t]:
308    ///   The file for which extended attributes will be deleted
309    /// - [`DUP`][crate::es_event_dup_t]:
310    ///   The file being duplicated
311    /// - [`UIPC_BIND`][crate::es_event_uipc_bind_t]:
312    ///   The path to the unix socket that will be created
313    /// - [`UIPC_CONNECT`][crate::es_event_uipc_connect_t]:
314    ///   The file that the unix socket being connected is bound to
315    /// - [`EXCHANGEDATA`][crate::es_event_exchangedata_t]:
316    ///   The path of both file1 and file2
317    /// - [`SETACL`][crate::es_event_setacl_t]:
318    ///   The file for which ACLs are being set
319    /// - [`PROC_CHECK`][crate::es_event_proc_check_t]:
320    ///   The path of the process against which access is being checked
321    /// - [`SEARCHFS`][crate::es_event_searchfs_t]:
322    ///   The path of the volume which will be searched
323    /// - [`PROC_SUSPEND_RESUME`][crate::es_event_proc_suspend_resume_t]:
324    ///   The path of the process being suspended or resumed
325    /// - [`GET_TASK_NAME`][crate::es_event_get_task_name_t]:
326    ///   The path of the process for which the task name port will be retrieved
327    /// - [`TRACE`][crate::es_event_trace_t]:
328    ///   The path of the process that will be attached to
329    /// - [`REMOTE_THREAD_CREATE`][crate::es_event_remote_thread_create_t]:
330    ///   The path of the process in which the new thread is created
331    /// - [`GET_TASK_READ`][crate::es_event_get_task_read_t]:
332    ///   The path of the process for which the task read port will be retrieved
333    /// - [`GET_TASK_INSPECT`][crate::es_event_get_task_inspect_t]:
334    ///   The path of the process for which the task inspect port will be retrieved
335    /// - [`COPYFILE`][crate::es_event_copyfile_t]:
336    ///   The path to the source file and the path to either the new file to be created or the
337    ///   existing file to be overwritten
338    #[cfg(feature = "macos_12_0_0")]
339    pub fn es_mute_path(client: *mut es_client_t, path: *const c_char, type_: es_mute_path_type_t) -> es_return_t;
340
341    /// Suppress a subset of events matching a path.
342    ///
343    /// - `client`: The es_client_t for which the path will be muted.
344    /// - `path`: The path to mute.
345    /// - `type`: Describes the type of the `path` parameter, either a prefix path or literal path.
346    /// - `events`: Array of event types for which the path should be muted.
347    /// - `event_count`: The number of items in the `events` array.
348    ///
349    /// See [`es_mute_path()`]
350    ///
351    /// When using `ES_MUTE_PATH_TYPE_TARGET_PREFIX` and `ES_MUTE_PATH_TYPE_TARGET_LITERAL` not
352    /// all events are supported. Target muting a path for an event type that does not support
353    /// target muting is a no-op. If at least one event type was muted for a target path then
354    /// `ES_RETURN_SUCCESS` is returned. If all specified event types do not support target muting
355    /// `ES_RETURN_ERROR` is returned. See [`es_mute_path()`] for the list of events that support
356    /// target path muting.
357    #[cfg(feature = "macos_12_0_0")]
358    pub fn es_mute_path_events(
359        client: *mut es_client_t,
360        path: *const c_char,
361        type_: es_mute_path_type_t,
362        events: *const es_event_type_t,
363        event_count: usize,
364    ) -> es_return_t;
365
366    /// Suppress events matching a path prefix
367    ///
368    #[cfg_attr(
369        feature = "macos_12_0_0",
370        doc = "**Deprecated in macOS 12**: Please use [`es_mute_path()`] or [`es_mute_path_events()`]"
371    )]
372    #[cfg_attr(
373        not(feature = "macos_12_0_0"),
374        doc = "**Deprecated in macOS 12**: Please use `es_mute_path()` or `es_mute_path_events()`"
375    )]
376    ///
377    /// - `client`: The client for which events will be suppressed
378    /// - `path_prefix`: The path against which suppressed executables must prefix match
379    pub fn es_mute_path_prefix(client: *mut es_client_t, path_prefix: *const c_char) -> es_return_t;
380
381    /// Suppress events matching a path literal
382    ///
383    #[cfg_attr(
384        feature = "macos_12_0_0",
385        doc = "**Deprecated in macOS 12**: Please use [`es_mute_path()`] or [`es_mute_path_events()`]"
386    )]
387    #[cfg_attr(
388        not(feature = "macos_12_0_0"),
389        doc = "**Deprecated in macOS 12**: Please use `es_mute_path()` or `es_mute_path_events()`"
390    )]
391    ///
392    /// - `client`: The client for which events will be suppressed
393    /// - `path_literal`: The path against which suppressed executables must match exactly
394    ///
395    #[cfg_attr(
396        feature = "macos_12_0_0",
397        doc = "See [`es_mute_path()`] and [`es_mute_path_events()`]"
398    )]
399    #[cfg_attr(
400        not(feature = "macos_12_0_0"),
401        doc = "See `es_mute_path()` and `es_mute_path_events()`"
402    )]
403    pub fn es_mute_path_literal(client: *mut es_client_t, path_literal: *const c_char) -> es_return_t;
404
405    /// Unmute all paths
406    ///
407    /// - `client`: The client for which all currently muted paths will be unmuted
408    ///
409    #[cfg_attr(
410        feature = "macos_13_0_0",
411        doc = "Only unmutes **executable** paths. To unmute target paths see [`es_unmute_all_target_paths()`]."
412    )]
413    #[cfg_attr(
414        not(feature = "macos_13_0_0"),
415        doc = "Only unmutes **executable** paths. To unmute target paths see `es_unmute_all_target_paths()`."
416    )]
417    pub fn es_unmute_all_paths(client: *mut es_client_t) -> es_return_t;
418
419    /// Unmute all target paths
420    ///
421    /// - `client`: The client for which all currently muted target paths will be unmuted
422    ///
423    /// See [`es_unmute_all_paths()`]
424    #[cfg(feature = "macos_13_0_0")]
425    pub fn es_unmute_all_target_paths(client: *mut es_client_t) -> es_return_t;
426
427    /// Unmute a path for all event types.
428    ///
429    /// - `client`: The es_client_t for which the path will be unmuted.
430    /// - `path`: The path to unmute.
431    /// - `type`: Describes the type of the `path` parameter, either a prefix path or literal path.
432    ///
433    /// Muting and unmuting operations logically work on a set of `(path_type, path,
434    /// es_event_type_t)` tuples Subtracting an element from the set that is not present has no
435    /// effect For example if `(literal, /foo/bar/, *)` is muted Then `(prefix, /foo, *)` is unmuted
436    /// the mute set is still: `(literal, /foo/bar, *)`. Prefixes only apply to mute evaluation not
437    /// to modifications of the mute set.
438    ///
439    /// See [`es_unmute_path_events()`]
440    #[cfg(feature = "macos_12_0_0")]
441    pub fn es_unmute_path(client: *mut es_client_t, path: *const c_char, type_: es_mute_path_type_t) -> es_return_t;
442
443    /// Unmute a path for a subset of event types.
444    ///
445    /// - `client`: The es_client_t for which the path will be unmuted.
446    /// - `path`: The path to unmute.
447    /// - `type`: Describes the type of the `path` parameter, either a prefix path or literal path.
448    /// - `events`: Array of event types for which the path should be unmuted.
449    /// - `event_count`: The number of items in the `events` array.
450    ///
451    /// See [`es_unmute_path()`]
452    #[cfg(feature = "macos_12_0_0")]
453    pub fn es_unmute_path_events(
454        client: *mut es_client_t,
455        path: *const c_char,
456        type_: es_mute_path_type_t,
457        events: *const es_event_type_t,
458        event_count: usize,
459    ) -> es_return_t;
460
461    /// Retrieve a list of all muted paths.
462    ///
463    /// - `client`: The es_client_t for which the muted paths will be retrieved.
464    /// - `muted_paths`: OUT param the will contain newly created memory describing the set of
465    ///   muted paths. This memory must be deleted using [`es_release_muted_paths()`].
466    ///
467    /// See [`es_release_muted_paths()`]
468    #[cfg(feature = "macos_12_0_0")]
469    pub fn es_muted_paths_events(client: *mut es_client_t, muted_paths: *mut *mut es_muted_paths_t) -> es_return_t;
470
471    /// Delete a set of muted paths obtained from `es_muted_paths_events`, freeing resources.
472    ///
473    /// - `muted_paths`: A set of muted paths to delete.
474    ///
475    /// See [`es_muted_paths_events()`]
476    #[cfg(feature = "macos_12_0_0")]
477    pub fn es_release_muted_paths(muted_paths: *mut es_muted_paths_t);
478
479    /// Invert the mute state of a given mute dimension
480    ///
481    /// - `client`: The `es_client_t` for which muting will be inverted
482    /// - `mute_type`: The type of muting to invert (process, path, or target path).
483    ///
484    /// Inverting muting can be used to create a client that monitors a specific process(es) or set
485    /// of directories When muting is inverted it still combines with other types of muting using
486    /// `OR`, and inversion happens first.
487    ///
488    /// Consider a series of inputs for a system where PID 12 is muted, process muting is inverted,
489    /// and `/bin/bash` is also path muted:
490    ///
491    /// - `(12, /bin/foo)  MATCHING (true, false)  INVERSION (false, false) || false` → event is **not** suppressed
492    /// - `(13, /bin/foo)  MATCHING (false, false) INVERSION (true, false)  || true`  → event is suppressed
493    /// - `(12, /bin/bash) MATCHING (true, true)   INVERSION (false, true)  || true`  → event is suppressed
494    ///
495    ///   Note that because muting is combined using OR even when pid 12 is being selected using
496    ///   inverted process muting, (12, /bin/bash) is still suppressed because the path is muted
497    ///
498    /// The relationship between all three types of muting (proc,path,target-path) and how each
499    /// can be inverted is complex. The below flow chart explains in detail exactly how muting is
500    /// applied in the kernel:
501    ///
502    /// ```text
503    /// ┌──────────────────┐
504    /// │      Event       │
505    /// └──────────────────┘
506    ///           │
507    ///           ▼
508    /// ┌──────────────────┐                                           ┌──────────────────┐
509    /// │  Is Subscribed?  │────No────────────────────────────────────▶│  Suppress Event  │
510    /// └──────────────────┘                                           └──────────────────┘
511    ///           │                                                              ▲
512    ///        Yes│                                                              │
513    ///           ▼                ┌────────────────┐                            │
514    /// ┌──────────────────┐       │ Is Proc Muting │                            │
515    /// │  Is Proc Muted?  ├─Yes──▶│   Inverted?    ├──No───────────────────────▶│
516    /// └─────────┬────────┘       └────────────────┘                            │
517    ///           │                         │                                    │
518    ///         No│                        Yes                                   │
519    ///           ▼                         │                                    │
520    /// ┌──────────────────┐                │                                    │
521    /// │  Is Proc Muting  │                │                                    │
522    /// │    Inverted?     │──Yes───────────)───────────────────────────────────▶│
523    /// └─────────┬────────┘                │                                    │
524    ///           │                         │                                    │
525    ///         No│◀────────────────────────┘                                    │
526    ///           ▼                 ┌───────────────┐                            │
527    /// ┌──────────────────┐        │Is Path Muting │                            │
528    /// │  Is Path Muted?  │──Yes──▶│   Inverted?   ├──No───────────────────────▶│
529    /// └─────────┬────────┘        └───────┬───────┘                            │
530    ///           │                         │                                    │
531    ///         No│                        Yes                                   │
532    ///           ▼                         │                                    │
533    /// ┌──────────────────┐                │                                    │
534    /// │  Is Path Muting  │                │                                    │
535    /// │    Inverted?     │──Yes───────────)───────────────────────────────────▶│
536    /// └─────────┬────────┘                │                                    │
537    ///           │                         │                                    │
538    ///         No│◀────────────────────────┘                                    │
539    ///           ▼                                                              │
540    /// ┌──────────────────┐                                                     │
541    /// │  Event Supports  │      ┌───────────────┐      ┌─────────────────┐     │
542    /// │   Target Path    │─Yes─▶│Is Target Path ├─Yes─▶│ Are ANY target  ├─No─▶│
543    /// │     Muting?      │      │Muting Inverted│      │  paths muted?   │     │
544    /// └──────────────────┘      └──────┬────────┘      └───────┬─────────┘     │
545    ///           │                      │                       │               │
546    ///         No│                    No│                      Yes              │
547    ///           │                      ▼                       │               │
548    ///           │              ┌────────────────┐              │               │
549    ///           │              │ Are ALL target │              │               │
550    ///           │              │  paths muted?  ├─Yes──────────)───────────────┘
551    ///           │              └───────┬────────┘              │
552    ///           │                      │                       │
553    ///           │                    No│                       │
554    ///           │◀─────────────────────┘                       │
555    ///           │                                              │
556    ///           │◀─────────────────────────────────────────────┘
557    ///           │
558    ///           ▼
559    /// ┌──────────────────┐
560    /// │  Deliver Event   │
561    /// └──────────────────┘
562    /// ```
563    ///
564    /// Mute inversion does **NOT** clear the default mute set. When a new `es_client_t` is created
565    /// certain paths are muted by default. This is known as "the default mute set". The default
566    /// mute set exists to protect ES clients from deadlocks, and to prevent watchdog timeout
567    /// panics. Creating a new client and calling `es_invert_muting(c, ES_MUTE_INVERSION_TYPE_PATH)`
568    /// will result in the default mute set being selected rather than muted. In most cases this
569    /// is unintended.
570    ///
571    /// - Consider calling [`es_unmute_all_paths()`] before inverting process path muting.
572    /// - Consider calling [`es_unmute_all_target_paths()`] before inverting target path muting.
573    ///
574    /// Make sure the client has no AUTH subscriptions before doing so. If desired the default mute
575    /// set can be saved using [`es_muted_paths_events()`] and then restored after inverting again.
576    #[cfg(feature = "macos_13_0_0")]
577    pub fn es_invert_muting(client: *mut es_client_t, mute_type: es_mute_inversion_type_t) -> es_return_t;
578
579    /// Query mute inversion state
580    ///
581    /// - `client`: The `es_client_t` for which mute inversion state is being queried.
582    /// - `mute_type`: The type of muting to query (process, path, or target path).
583    #[cfg(feature = "macos_13_0_0")]
584    pub fn es_muting_inverted(
585        client: *mut es_client_t,
586        mute_type: es_mute_inversion_type_t,
587    ) -> es_mute_inverted_return_t;
588
589    /// Clear all cached results for all clients.
590    ///
591    /// - `client`: that will perform the request
592    ///
593    /// This functions clears the shared cache for all ES clients and is hence
594    /// rate limited. If `es_clear_cache` is called too frequently it will return
595    /// `ES_CLEAR_CACHE_RESULT_ERR_THROTTLE`.
596    ///
597    /// It is permissible to pass any valid `es_client_t` object created by `es_new_client`.
598    pub fn es_clear_cache(client: *mut es_client_t) -> es_clear_cache_result_t;
599}
600
601/// The type of block that will be invoked to handled messages from the ES subsystem
602///
603/// - The `es_client_t` is a handle to the client being sent the event. It must be passed to any
604///   "respond" functions
605/// - The `es_message_t` is the message that must be handled. Mutating it is forbidden but Rust
606///   does not expose a `ConstNonNull` type.
607pub type es_handler_block_t<'f> = Block<dyn Fn(NonNull<es_client_t>, NonNull<es_message_t>) + 'f>;
608
609#[link(name = "EndpointSecurity", kind = "dylib")]
610extern "C" {
611    /// Initialise a new `es_client_t` and connect to the ES subsystem
612    ///
613    /// - `client`: Out param. On success this will be set to point to the newly allocated [`es_client_t`].
614    /// - `handler`: The handler block that will be run on all messages sent to this client
615    ///
616    /// Messages are handled strictly serially and in the order they are delivered. Returning
617    /// control from the handler causes the next available message to be dequeued. Messages can
618    /// be responded to out of order by returning control before calling `es_respond_*`. The
619    /// `es_message_t` is only guaranteed to live as long as the scope it is passed into. The
620    /// memory for the given `es_message_t` is NOT owned by clients and it must not be freed. For
621    /// out of order responding the handler must retain the message with [`es_retain_message()`][erm].
622    /// Callers are required to be entitled with `com.apple.developer.endpoint-security.client`. The
623    /// application calling this interface must also be approved by users via Transparency, Consent
624    /// & Control (TCC) mechanisms using the Privacy Preferences pane and adding the application
625    /// to Full Disk Access. When a new client is successfully created, all cached results are
626    /// automatically cleared.
627    ///
628    /// When a new client is initialized, there will be a set of paths and a subset of
629    /// `es_event_type_t` events that are automatically muted by default. Generally, most AUTH event
630    /// variants are muted but NOTIFY event variants will still be sent to the client. The set of
631    /// paths muted by default are ones that can have an extremely negative impact to end users
632    /// if their AUTH events are not allowed in a timely manner (for example, executable paths for
633    /// processes that are monitored by the watchdogd daemon). It is important to understand that
634    /// this list is *not* exhaustive and developers using the EndpointSecurity framework can still
635    /// interfere with critical system components and must use caution to limit user impact. The set
636    /// of default muted paths and event types may change across macOS releases. It is possible to
637    /// both inspect and unmute the set of default muted paths and associated event types using the
638    /// appropriate mute-related API, however it is not recommended to unmute these items.
639    ///
640    /// The only supported way to check if an application is properly TCC authorized for Full Disk
641    /// Access is to call `es_new_client()` and handling [`ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED`][not-perm]
642    /// in a way appropriate to your application. Most applications will want to ask the user for TCC
643    /// authorization when es_new_client returns [`ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED`][not-perm].
644    /// To direct the user to the Full Disk Access section in System Settings, applications can use
645    /// the following URLs:
646    ///
647    /// - `x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_AllFiles` (macOS 13 and later)
648    /// - `x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles` (until macOS 12)
649    ///
650    /// Applications are advised to use the new URL in macOS 13 as the old one may stop working in a
651    /// future release.
652    ///
653    /// See also:
654    ///
655    /// - [`es_retain_message()`][erm]
656    #[cfg_attr(feature = "macos_11_0_0", doc = "- [`es_release_message()`][rls]")]
657    #[cfg_attr(not(feature = "macos_11_0_0"), doc = "- `es_release_message()`")]
658    /// - [`es_new_client_result_t`]
659    #[cfg_attr(
660        feature = "macos_12_0_0",
661        doc = "- [`es_muted_paths_events()`]\n- [`es_unmute_path_events()`]"
662    )]
663    #[cfg_attr(
664        not(feature = "macos_12_0_0"),
665        doc = "- `es_muted_paths_events()`\n- `es_unmute_path_events()`"
666    )]
667    ///
668    /// [erm]: crate::es_retain_message
669    /// [rls]: crate::es_release_message
670    /// [not-perm]: crate::es_new_client_result_t::ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED
671    #[allow(improper_ctypes)]
672    // In this specific case, it is okay because the block is called as
673    // an Objc-C block (a closure), it's not modified/dereferenced.
674    pub fn es_new_client(client: *mut *mut es_client_t, handler: &es_handler_block_t) -> es_new_client_result_t;
675
676    /// Destroy an `es_client_t`, freeing resources and disconnecting from the ES subsystem
677    ///
678    /// - `client`: The client to be destroyed
679    ///
680    /// - `ES_RETURN_SUCCESS` indicates all resources were freed.
681    /// - `ES_RETURN_ERROR` indicates an error occurred during shutdown and resources were leaked.
682    ///
683    /// Must be called from the same thread that originally called [`es_new_client()`].
684    pub fn es_delete_client(client: *mut es_client_t) -> es_return_t;
685}