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}