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}