Skip to main content

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