├── .gitignore
├── examples
├── Extension.entitlements
├── process_monitor.rs
└── signal_intercept.rs
├── Cargo.toml
├── README.md
├── LICENSE
└── src
├── parsers.rs
└── lib.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | Cargo.lock
3 | *.swp
4 |
--------------------------------------------------------------------------------
/examples/Extension.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.application-identifier
6 | 5QYJ6C8ZNT.*
7 | com.apple.developer.endpoint-security.client
8 |
9 | com.apple.developer.team-identifier
10 | 5QYJ6C8ZNT
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "endpointsecurity"
3 | version = "0.3.0"
4 | authors = ["Mitchell Grenier "]
5 | edition = "2018"
6 | description = "Nice (ish) bindings for the EndpointSecurity framework on macOS"
7 | license-file = "LICENSE"
8 | keywords = ["security", "macos", "endpoint"]
9 | readme = "README.md"
10 | homepage = "https://github.com/obelisk/endpointsecurity"
11 |
12 | [dependencies]
13 | crossbeam-channel = "0.5"
14 | block = "0.1.6"
15 | env_logger = "0.7.1"
16 | libc = "0.2.0"
17 | log = "0.4.8"
18 | serde = {version = "1", features = ["derive"]}
19 |
20 | [dev-dependencies]
21 | serde_json = "1"
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EndpointSecurity in Rust
2 |
3 | This is a crate I wrote to power building EndpointSecurity products in Rust. This is still very much in active development and subject to significant changes (though the existing pub api I would expect to stay more or less the same).
4 |
5 | EsClient is marked Send and Sync because to the best of my knowledge, it is. There are locks around using it inside the API because I don't know if EndpointSecurity is threadsafe. Your mileage may vary.
6 |
7 | Not all calls are implemented, only the ones I've needed so far and some of the ones that are implemented are incomplete (most of the important stuff is there though).
8 |
9 | Licenced under MIT because free software is important.
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Mitchell Grenier
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/process_monitor.rs:
--------------------------------------------------------------------------------
1 | #[macro_use] extern crate log;
2 |
3 | use endpointsecurity::*;
4 | use crossbeam_channel::unbounded as channel;
5 |
6 | fn main() {
7 | env_logger::init();
8 | info!("Starting example process monitor");
9 | let (es_message_tx, es_message_rx) = channel();
10 |
11 | let client = match create_es_client(es_message_tx.clone()) {
12 | Ok(client) => client,
13 | Err(e) => {
14 | error!("{:?}: {}", e.error_type, e.details);
15 | return;
16 | }
17 | };
18 |
19 | if !client.set_subscriptions_to(&vec![SupportedEsEvent::NotifyExec, SupportedEsEvent::NotifyFork]) {
20 | error!("Could not subscribe to NotifyExec event (not sure why)");
21 | return;
22 | }
23 |
24 | loop {
25 | let message = match es_message_rx.recv() {
26 | Ok(v) => v,
27 | Err(e) => {
28 | error!("Error receiving new event but will continue anyway: {}", e);
29 | continue;
30 | }
31 | };
32 |
33 | match &message.event {
34 | EsEvent::NotifyExec(event) => {
35 | match serde_json::to_string(&event) {
36 | Ok(json) => println!("{}", json),
37 | Err(e) => error!("Error serializing event: {}", e)
38 | }
39 | //println!("Type: Exec, PID: {}, Path: {}, CDHash: {}, Args: {}", event.target.pid, event.target.executable.path, event.target.cdhash, event.args.join(" "));
40 | },
41 | EsEvent::NotifyFork(event) => {
42 | match serde_json::to_string(&event) {
43 | Ok(json) => println!("{}", json),
44 | Err(e) => error!("Error serializing event: {}", e)
45 | }
46 | //println!("Type: Fork, PID: {}, Path: {}, CDHash: {}", event.child.pid, event.child.executable.path, event.child.cdhash);
47 | },
48 | _ => {
49 | continue;
50 | }
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/examples/signal_intercept.rs:
--------------------------------------------------------------------------------
1 | #[macro_use] extern crate log;
2 |
3 | use endpointsecurity::*;
4 | use crossbeam_channel::unbounded as channel;
5 |
6 | fn main() {
7 | env_logger::init();
8 | info!("Starting example signal intercept handler");
9 |
10 | let (es_message_tx, es_message_rx) = channel();
11 |
12 | let client = match create_es_client(es_message_tx.clone()) {
13 | Ok(client) => client,
14 | Err(e) => {
15 | error!("{:?}: {}", e.error_type, e.details);
16 | return;
17 | }
18 | };
19 |
20 | if !client.set_subscriptions_to(&vec![SupportedEsEvent::AuthSignal]) {
21 | error!("Could not subscribe to AuthSignal event (not sure why)");
22 | return;
23 | }
24 |
25 | loop {
26 | let message = match es_message_rx.recv() {
27 | Ok(v) => v,
28 | Err(e) => {
29 | error!("Error receiving new event but will continue anyway: {}", e);
30 | continue;
31 | }
32 | };
33 |
34 | // We don't cache the result here for demonstation purposes. In practice you should
35 | // cache whenever possible but here we don't so you can see every denial instead of
36 | // just the first one
37 | match &message.event {
38 | EsEvent::AuthSignal(event) => {
39 | // Backout of all signals that don't affect EsClients as quickly as possible
40 | // to reduce impact to system responsiveness
41 | if !event.target.is_es_client {
42 | client.respond_to_auth_event(&message, &EsAuthResult::Allow, &EsCacheResult::No);
43 | continue;
44 | }
45 |
46 | // My signing ID, don't let signals reach my EsClients
47 | if event.target.team_id == "5QYJ6C8ZNT" {
48 | println!("Received a signal to my EsClient, disallowing that!");
49 | client.respond_to_auth_event(&message, &EsAuthResult::Deny, &EsCacheResult::No);
50 | }
51 |
52 | // This is a signal to someone else's EsClient. Don't touch it
53 | client.respond_to_auth_event(&message, &EsAuthResult::Allow, &EsCacheResult::No);
54 | },
55 | _ => {
56 | continue;
57 | }
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/src/parsers.rs:
--------------------------------------------------------------------------------
1 | use std::ffi::CStr;
2 | use crate::{
3 | audit_token_t,
4 | es_action_type_t_ES_ACTION_TYPE_AUTH as ES_ACTION_TYPE_AUTH,
5 | es_action_type_t_ES_ACTION_TYPE_NOTIFY as ES_ACTION_TYPE_NOTIFY,
6 | es_events_t,
7 | es_exec_arg,
8 | es_exec_arg_count,
9 | es_file_t,
10 | es_message_t,
11 | es_message_t__bindgen_ty_1,
12 | es_process_t,
13 | es_string_token_t,
14 | pid_t,
15 | };
16 |
17 | // Non Event Structures
18 | use crate::{
19 | EsAction,
20 | EsActionType,
21 | EsEvent,
22 | EsEventId, // I know this is inconsistent but I don't have a better name for this struct
23 | EsFile,
24 | EsMessage,
25 | EsRenameDestination,
26 | EsRenameDestinationNewPath,
27 | EsResult,
28 | EsResultType,
29 | EsResultNotifyResult,
30 | EsDestinationType,
31 | EsProcess,
32 | SupportedEsEvent,
33 | };
34 |
35 | // Event Structures
36 | use crate::{
37 | EsEventClose,
38 | EsEventExec,
39 | EsEventFork,
40 | EsEventKextload,
41 | EsEventKextunload,
42 | EsEventLink,
43 | EsEventOpen,
44 | EsEventReadDir,
45 | EsEventRename,
46 | EsEventSignal,
47 | EsEventUnlink,
48 | };
49 |
50 | use crate::raw_event_to_supportedesevent;
51 |
52 | extern "C" {
53 | pub fn audit_token_to_pid(audit_token: audit_token_t) -> pid_t;
54 | }
55 |
56 | /// Take in an es_event_t from the EndpointSecurity Framework,
57 | /// and parse it into a safe Rust structure.
58 | fn parse_es_action(action: es_message_t__bindgen_ty_1, action_type: &EsActionType) -> Option {
59 | Some(match action_type {
60 | EsActionType::Auth => EsAction::Auth(EsEventId{
61 | reserved: unsafe {action.auth.reserved},
62 | }),
63 | EsActionType::Notify => EsAction::Notify(EsResult {
64 | result_type: {
65 | match unsafe {action.notify.result_type} {
66 | 0 => EsResultType::Auth,
67 | 1 => EsResultType::Flags,
68 | _ => {
69 | error!("Result Type is broken");
70 | return None; // At time of writing these are the only types
71 | }
72 | }
73 | },
74 | result: EsResultNotifyResult {
75 | flags: unsafe { action.notify.result.flags },
76 | }
77 | })
78 | })
79 | }
80 |
81 | /// Take in an es_event_t from the EndpointSecurity Framework,
82 | /// and parse it into a safe Rust structure.
83 | fn parse_es_event(event_type: SupportedEsEvent, event: es_events_t, action_type: &EsActionType) -> EsEvent {
84 | unsafe {
85 | match event_type {
86 | SupportedEsEvent::AuthExec | SupportedEsEvent::NotifyExec => {
87 | let target = event.exec.target;
88 | let argc = es_exec_arg_count(&event.exec as *const _);
89 | let mut argv = vec![];
90 | argv.reserve(argc as usize);
91 | let mut x = 0;
92 | while x < argc {
93 | argv.push(parse_es_string_token(es_exec_arg(&event.exec as *const _, x as u32)));
94 | x += 1;
95 | }
96 |
97 | let event = EsEventExec {
98 | target: parse_es_process(&*target),
99 | args: argv,
100 | };
101 |
102 | match action_type {
103 | EsActionType::Notify => EsEvent::NotifyExec(event),
104 | EsActionType::Auth => EsEvent::AuthExec(event),
105 | }
106 | },
107 | SupportedEsEvent::AuthOpen | SupportedEsEvent::NotifyOpen => {
108 | let file = event.open;
109 | let event = EsEventOpen {
110 | fflag: file.fflag as u32,
111 | file: parse_es_file(file.file),
112 | };
113 | match action_type {
114 | EsActionType::Notify => EsEvent::NotifyOpen(event),
115 | EsActionType::Auth => EsEvent::AuthOpen(event),
116 | }
117 | },
118 | SupportedEsEvent::AuthKextload | SupportedEsEvent::NotifyKextload => {
119 | let load = event.kextload;
120 | let event = EsEventKextload {
121 | identifier: parse_es_string_token(load.identifier),
122 | };
123 | match action_type {
124 | EsActionType::Notify => EsEvent::NotifyKextload(event),
125 | EsActionType::Auth => EsEvent::AuthKextload(event),
126 | }
127 | },
128 | SupportedEsEvent::AuthSignal | SupportedEsEvent::NotifySignal => {
129 | let target = event.signal.target;
130 | let event = EsEventSignal {
131 | signal: event.signal.sig,
132 | target: parse_es_process(&*target),
133 | };
134 | match action_type {
135 | EsActionType::Notify => EsEvent::NotifySignal(event),
136 | EsActionType::Auth => EsEvent::AuthSignal(event),
137 | }
138 | },
139 | SupportedEsEvent::AuthUnlink | SupportedEsEvent::NotifyUnlink => {
140 | let target = event.unlink.target;
141 | let parent_dir = event.unlink.target;
142 | let event = EsEventUnlink {
143 | target: parse_es_file(target),
144 | parent_dir: parse_es_file(parent_dir),
145 | };
146 | match action_type {
147 | EsActionType::Notify => EsEvent::NotifyUnlink(event),
148 | EsActionType::Auth => EsEvent::AuthUnlink(event),
149 | }
150 | },
151 | SupportedEsEvent::AuthLink | SupportedEsEvent::NotifyLink => {
152 | let event = EsEventLink {
153 | source: parse_es_file(event.link.source),
154 | target_dir: parse_es_file(event.link.target_dir),
155 | target_filename: parse_es_string_token(event.link.target_filename),
156 | };
157 | match action_type {
158 | EsActionType::Notify => EsEvent::NotifyLink(event),
159 | EsActionType::Auth => EsEvent::AuthLink(event),
160 | }
161 | },
162 | SupportedEsEvent::AuthRename | SupportedEsEvent::NotifyRename => {
163 | let event = EsEventRename {
164 | source: parse_es_file(event.rename.source),
165 | destination_type: match event.rename.destination_type {
166 | 0 => EsDestinationType::ExistingFile,
167 | 1 => EsDestinationType::NewPath,
168 | _ => EsDestinationType::Unknown,
169 | },
170 | destination: EsRenameDestination {
171 | existing_file: parse_es_file(event.rename.destination.existing_file),
172 | new_path: EsRenameDestinationNewPath {
173 | dir: parse_es_file(event.rename.destination.new_path.dir),
174 | filename: parse_es_string_token(event.rename.destination.new_path.filename),
175 | },
176 | }
177 | };
178 | match action_type {
179 | EsActionType::Notify => EsEvent::NotifyRename(event),
180 | EsActionType::Auth => EsEvent::AuthRename(event),
181 | }
182 | },
183 | SupportedEsEvent::AuthReadDir | SupportedEsEvent::NotifyReadDir => {
184 | let event = EsEventReadDir {
185 | target: parse_es_file(event.readdir.target),
186 | };
187 | match action_type {
188 | EsActionType::Notify => EsEvent::NotifyReadDir(event),
189 | EsActionType::Auth => EsEvent::AuthReadDir(event),
190 | }
191 | },
192 | SupportedEsEvent::NotifyFork => {
193 | EsEvent::NotifyFork(EsEventFork {
194 | child: parse_es_process(&*event.fork.child),
195 | })
196 | },
197 | SupportedEsEvent::NotifyKextunload => {
198 | EsEvent::NotifyKextunload(EsEventKextunload {
199 | identifier: parse_es_string_token(event.kextunload.identifier),
200 | })
201 | },
202 | SupportedEsEvent::NotifyClose => {
203 | EsEvent::NotifyClose(EsEventClose {
204 | modified: event.close.modified,
205 | target: parse_es_file(event.close.target),
206 | })
207 | },
208 | }
209 | }
210 | }
211 |
212 | /// Take in an es_file_t from the EndpointSecurity Framework,
213 | /// and parse it into a safe Rust structure.
214 | fn parse_es_file(file: *mut es_file_t) -> EsFile {
215 | let f = unsafe {*file};
216 | EsFile {
217 | path: unsafe { CStr::from_ptr(f.path.data).to_str().unwrap().to_owned() },
218 | path_truncated: { f.path_truncated },
219 | }
220 | }
221 |
222 | /// Take in an es_message_t from the EndpointSecurity Framework,
223 | /// and parse it into a safe Rust structure.
224 | pub fn parse_es_message(message: *mut es_message_t) -> Result {
225 | let message = unsafe {&*message};
226 | let process = unsafe {&*(message.process)};
227 | let action_type = match message.action_type {
228 | ES_ACTION_TYPE_AUTH => EsActionType::Auth,
229 | ES_ACTION_TYPE_NOTIFY => EsActionType::Notify,
230 | _ => return Err("Couldn't parse action_type"), // At time of writing these are the only two ways
231 | };
232 |
233 | let event = if let Some(event) = raw_event_to_supportedesevent(message.event_type as u64) {
234 | parse_es_event(event, message.event, &action_type)
235 | } else {
236 | error!("Error in this event type: {}", message.event_type as u64);
237 | return Err("Could not parse this event type");
238 | };
239 |
240 | Ok(EsMessage {
241 | version: message.version,
242 | time: message.time.tv_sec as u64,
243 | mach_time: message.mach_time,
244 | deadline: message.deadline,
245 | process: parse_es_process(process),
246 | seq_num: message.seq_num,
247 | action: match parse_es_action(message.action, &action_type) {
248 | Some(x) => x,
249 | None => return Err("Couldn't parse the action field"),
250 | },
251 | action_type: action_type,
252 | event: event,
253 | raw_message: message,
254 | })
255 | }
256 |
257 | /// Take in an es_process_t from the EndpointSecurity Framework,
258 | /// and parse it into a safe Rust structure.
259 | fn parse_es_process(process: &es_process_t) -> EsProcess {
260 | EsProcess {
261 | ppid: process.ppid as u32,
262 | original_ppid: process.original_ppid as u32,
263 | pid: unsafe { audit_token_to_pid(process.audit_token) as u32 },
264 | group_id: process.group_id as u32,
265 | session_id: process.session_id as u32,
266 | codesigning_flags: process.codesigning_flags as u32,
267 | is_platform_binary: process.is_platform_binary,
268 | is_es_client: process.is_es_client,
269 | cdhash: {
270 | let mut x = String::new();
271 | x.reserve(40);
272 | for byte in &process.cdhash {
273 | x.push_str(format!("{:02X}", byte).as_str());
274 | }
275 | x
276 | },
277 | signing_id: parse_es_string_token(process.signing_id),
278 | team_id: parse_es_string_token(process.team_id),
279 | executable: parse_es_file(process.executable),
280 | }
281 | }
282 |
283 | /// Take in an es_string_token from the EndpointSecurity Framework,
284 | /// and parse it into a safe Rust structure.
285 | fn parse_es_string_token(string_token: es_string_token_t) -> String {
286 | match string_token.length {
287 | x if x <= 0 => {
288 | String::new()
289 | },
290 | _ => {
291 | match unsafe { CStr::from_ptr(string_token.data).to_str() }{
292 | Ok(v) => v.to_owned(),
293 | Err(e) => {
294 | error!("String would not parse: {}", e);
295 | String::new()
296 | }
297 | }
298 | },
299 | }
300 | }
301 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | // Allow here to prevent compiler errors from the bindgen structs and functions we can't control
2 | #![allow(non_upper_case_globals)]
3 | #![allow(non_camel_case_types)]
4 | #![allow(non_snake_case)]
5 |
6 | include!("./eps_bindings.rs");
7 |
8 | mod parsers;
9 |
10 | extern crate libc;
11 | #[macro_use] extern crate log;
12 |
13 | use std::collections::HashSet;
14 | use std::fmt;
15 | use crossbeam_channel::Sender;
16 | use std::sync::{Arc, Mutex};
17 |
18 | use serde::{Deserialize, Serialize};
19 |
20 | use parsers::*;
21 |
22 | use block::*;
23 |
24 | // Values
25 | use {
26 | es_clear_cache_result_t_ES_CLEAR_CACHE_RESULT_SUCCESS as ES_CLEAR_CACHE_RESULT_SUCCESS,
27 | es_clear_cache_result_t_ES_CLEAR_CACHE_RESULT_ERR_INTERNAL as ES_CLEAR_CACHE_RESULT_ERR_INTERNAL,
28 | es_clear_cache_result_t_ES_CLEAR_CACHE_RESULT_ERR_THROTTLE as ES_CLEAR_CACHE_RESULT_ERR_THROTTLE,
29 |
30 | es_return_t_ES_RETURN_SUCCESS as ES_RETURN_SUCCESS,
31 |
32 | es_auth_result_t_ES_AUTH_RESULT_ALLOW as ES_AUTH_RESULT_ALLOW,
33 | es_auth_result_t_ES_AUTH_RESULT_DENY as ES_AUTH_RESULT_DENY,
34 |
35 | es_new_client_result_t_ES_NEW_CLIENT_RESULT_SUCCESS as ES_NEW_CLIENT_SUCCESS,
36 | es_new_client_result_t_ES_NEW_CLIENT_RESULT_ERR_INVALID_ARGUMENT as ES_NEW_CLIENT_ERROR_INVALID_ARGUMENT,
37 | es_new_client_result_t_ES_NEW_CLIENT_RESULT_ERR_INTERNAL as ES_NEW_CLIENT_ERROR_INTERNAL,
38 | es_new_client_result_t_ES_NEW_CLIENT_RESULT_ERR_NOT_ENTITLED as ES_NEW_CLIENT_ERROR_NOT_ENTITLED,
39 | es_new_client_result_t_ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED as ES_NEW_CLIENT_ERROR_NOT_PERMITTED,
40 | es_new_client_result_t_ES_NEW_CLIENT_RESULT_ERR_NOT_PRIVILEGED as ES_NEW_CLIENT_ERROR_NOT_PRIVILEGED,
41 | es_new_client_result_t_ES_NEW_CLIENT_RESULT_ERR_TOO_MANY_CLIENTS as ES_NEW_CLIENT_ERROR_TOO_MANY_CLIENTS,
42 |
43 | es_respond_result_t_ES_RESPOND_RESULT_SUCCESS as ES_RESPOND_RESULT_SUCCESS,
44 | es_respond_result_t_ES_RESPOND_RESULT_ERR_INVALID_ARGUMENT as ES_RESPONSE_RESULT_ERROR_INVALID_ARGUMENT,
45 | es_respond_result_t_ES_RESPOND_RESULT_ERR_INTERNAL as ES_RESPOND_RESULT_ERROR_INTERNAL,
46 | es_respond_result_t_ES_RESPOND_RESULT_NOT_FOUND as ES_RESPOND_RESULT_NOT_FOUND,
47 | es_respond_result_t_ES_RESPOND_RESULT_ERR_DUPLICATE_RESPONSE as ES_RESPOND_RESULT_ERROR_DUPLICATE_RESPONSE,
48 | es_respond_result_t_ES_RESPOND_RESULT_ERR_EVENT_TYPE as ES_RESPONSE_RESULT_ERROR_EVENT_TYPE,
49 | };
50 |
51 | #[repr(C)]
52 | #[derive(Debug, PartialEq)]
53 | pub enum FileModes {
54 | Read = 0x00000001,
55 | Write = 0x00000002,
56 | NonBlock = 0x00000004,
57 | Append = 0x00000008,
58 | }
59 |
60 | #[derive(Clone, Debug, Serialize, Deserialize)]
61 | pub struct EsFile {
62 | pub path: String,
63 | pub path_truncated: bool,
64 | // pub stat: stat,
65 | }
66 |
67 | #[derive(Clone, Debug, Serialize, Deserialize)]
68 | pub struct EsProcess {
69 | //pub audit_token: rust_audit_token,
70 | pub ppid: u32,
71 | pub original_ppid: u32,
72 | pub pid: u32,
73 | pub group_id: u32,
74 | pub session_id: u32,
75 | pub codesigning_flags: u32,
76 | pub is_platform_binary: bool,
77 | pub is_es_client: bool,
78 | pub cdhash: String,
79 | pub signing_id: String,
80 | pub team_id: String,
81 | pub executable: EsFile,
82 | //pub tty: EsFile,
83 | //pub start_time: timeval,
84 | }
85 |
86 | #[derive(Clone, Debug, Serialize, Deserialize)]
87 | pub struct EsEventExec {
88 | pub target: EsProcess,
89 | pub args: Vec,
90 | // __bindgen_anon_1: es_event_exec_t__bindgen_ty_1,
91 | }
92 |
93 | #[derive(Clone, Debug, Serialize, Deserialize)]
94 | pub struct EsEventFork {
95 | pub child: EsProcess,
96 | }
97 |
98 | #[derive(Clone, Debug, Serialize, Deserialize)]
99 | pub struct EsEventClose {
100 | pub modified: bool,
101 | pub target: EsFile,
102 | }
103 |
104 | #[derive(Clone, Debug, Serialize, Deserialize)]
105 | pub struct EsEventOpen {
106 | pub fflag: u32,
107 | pub file: EsFile,
108 | // reserved: [u8; 64usize],
109 | }
110 |
111 | #[derive(Clone, Debug, Serialize, Deserialize)]
112 | pub struct EsEventKextload {
113 | pub identifier: String,
114 | // reserved: [u8; 64usize],
115 | }
116 |
117 | #[derive(Clone, Debug, Serialize, Deserialize)]
118 | pub struct EsEventKextunload {
119 | pub identifier: String,
120 | // reserved: [u8; 64usize],
121 | }
122 |
123 | #[derive(Clone, Debug, Serialize, Deserialize)]
124 | pub struct EsEventSignal {
125 | pub signal: i32,
126 | pub target: EsProcess,
127 | //pub reserved: [u8; 64usize],
128 | }
129 |
130 | #[derive(Clone, Debug, Serialize, Deserialize)]
131 | pub struct EsEventLink {
132 | pub source: EsFile,
133 | pub target_dir: EsFile,
134 | pub target_filename: String,
135 | //pub reserved: [u8; 64usize],
136 | }
137 |
138 | #[derive(Clone, Debug, Serialize, Deserialize)]
139 | pub struct EsEventUnlink {
140 | pub target: EsFile,
141 | pub parent_dir: EsFile,
142 | //pub reserved: [u8; 64usize],
143 | }
144 |
145 | #[derive(Clone, Debug, Serialize, Deserialize)]
146 | pub enum EsDestinationType {
147 | ExistingFile = 0,
148 | NewPath = 1,
149 | Unknown = 2,
150 | }
151 |
152 | #[derive(Clone, Debug, Serialize, Deserialize)]
153 | pub struct EsRenameDestinationNewPath {
154 | pub dir: EsFile,
155 | pub filename: String,
156 | }
157 |
158 | #[derive(Clone, Debug, Serialize, Deserialize)]
159 | pub struct EsRenameDestination {
160 | pub existing_file: EsFile,
161 | pub new_path: EsRenameDestinationNewPath,
162 | }
163 |
164 | #[derive(Clone, Debug, Serialize, Deserialize)]
165 | pub struct EsEventRename {
166 | pub source: EsFile,
167 | pub destination_type: EsDestinationType,
168 | pub destination: EsRenameDestination,
169 | //pub reserved: [u8; 64usize],
170 | }
171 |
172 | #[derive(Clone, Debug, Serialize, Deserialize)]
173 | pub struct EsEventReadDir {
174 | pub target: EsFile,
175 | }
176 |
177 | #[derive(Debug, Serialize, Deserialize)]
178 | pub enum EsRespondResult {
179 | Sucess,
180 | ErrorInvalidArgument,
181 | ErrorInternal,
182 | NotFound,
183 | ErrorDuplicateResponse,
184 | ErrorEventType,
185 | UnknownResponse,
186 | }
187 |
188 | #[derive(Clone, Debug, Serialize, Deserialize)]
189 | pub enum EsNewClientResult {
190 | Success,
191 | ErrorInvalidArgument,
192 | ErrorInternal,
193 | ErrorNotEntitled,
194 | ErrorNotPermitted,
195 | ErrorNotPrivileged,
196 | ErrorTooManyClients,
197 | Unknown,
198 | }
199 |
200 | #[derive(Debug, Serialize, Deserialize)]
201 | pub enum ClearCacheResult {
202 | Success,
203 | ErrorInternal,
204 | ErrorThrottle,
205 | }
206 |
207 | #[derive(Clone, Debug, Serialize, Deserialize)]
208 | pub struct EsClientError {
209 | pub details: String,
210 | pub error_type: EsNewClientResult,
211 | }
212 |
213 | impl fmt::Display for EsClientError {
214 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215 | write!(f,"{}", self.details)
216 | }
217 | }
218 |
219 | #[repr(C)]
220 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
221 | pub enum SupportedEsEvent {
222 | AuthExec = 0,
223 | AuthOpen = 1,
224 | AuthKextload = 2,
225 | AuthRename = 6,
226 | AuthSignal = 7,
227 | AuthUnlink = 8,
228 | NotifyExec = 9,
229 | NotifyOpen = 10,
230 | NotifyFork = 11,
231 | NotifyClose = 12,
232 | NotifyKextload = 17,
233 | NotifyKextunload = 18,
234 | NotifyLink = 19,
235 | NotifyRename = 25,
236 | NotifySignal = 31,
237 | NotifyUnlink = 32,
238 | AuthLink = 42,
239 | AuthReadDir = 67,
240 | NotifyReadDir = 68,
241 | }
242 |
243 | impl fmt::Display for SupportedEsEvent {
244 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245 | write!(f, "{:?}", self)
246 | }
247 | }
248 |
249 | #[derive(Clone, Debug, Serialize, Deserialize)]
250 | pub enum EsEvent {
251 | AuthExec(EsEventExec),
252 | AuthOpen(EsEventOpen),
253 | AuthKextload(EsEventKextload),
254 | /*AuthMmap,
255 | AuthMprotect,
256 | AuthMount,*/
257 | AuthRename(EsEventRename),
258 | AuthSignal(EsEventSignal),
259 | AuthUnlink(EsEventUnlink),
260 | NotifyExec(EsEventExec),
261 | NotifyOpen(EsEventOpen),
262 | NotifyFork(EsEventFork),
263 | NotifyClose(EsEventClose),
264 | /*NotifyCreate,
265 | NotifyExchangedata,
266 | NotifyExit,
267 | NotifyGetTask,*/
268 | NotifyKextload(EsEventKextload),
269 | NotifyKextunload(EsEventKextunload),
270 | NotifyLink(EsEventLink),
271 | /*NotifyMmap,
272 | NotifyMprotect,
273 | NotifyMount,
274 | NotifyUnmount,
275 | NotifyIokitOpen,*/
276 | NotifyRename(EsEventRename),
277 | /*NotifySetattrlist,
278 | NotifySetextattr,
279 | NotifySetflags,
280 | NotifySetmode,
281 | NotifySetowner,*/
282 | NotifySignal(EsEventSignal),
283 | NotifyUnlink(EsEventUnlink),
284 | /*NotifyWrite,
285 | AuthFileProviderMaterialize,
286 | NotifyFileProviderMaterialize,
287 | AuthFileProviderUpdate,
288 | NotifyFileProviderUpdate,
289 | AuthReadlink,
290 | NotifyReadlink,
291 | AuthTruncate,
292 | NotifyTruncate,*/
293 | AuthLink(EsEventLink),
294 | /*NotifyLookup,
295 | AuthCreate,
296 | AuthSetattrlist,
297 | AuthSetextattr,
298 | AuthSetflags,
299 | AuthSetmode,
300 | AuthSetowner,
301 | AuthChdir,
302 | NotifyChdir,
303 | AuthGetattrlist,
304 | NotifyGetattrlist,
305 | NotifyStat,
306 | NotifyAccess,
307 | AuthChroot,
308 | NotifyChroot,
309 | AuthUtimes,
310 | NotifyUtimes,
311 | AuthClone,
312 | NotifyClone,
313 | NotifyFcntl,
314 | AuthGetextattr,
315 | NotifyGetextattr,
316 | AuthListextattr,
317 | NotifyListextattr,*/
318 | AuthReadDir(EsEventReadDir),
319 | NotifyReadDir(EsEventReadDir),
320 | /*AuthDeleteextattr,
321 | NotifyDeleteextattr,
322 | AuthFsgetpath,
323 | NotifyFsgetpath,
324 | NotifyDup,
325 | AuthSettime,
326 | NotifySettime,
327 | NotifyUipcBind,
328 | AuthUipcBind,
329 | NotifyUipcConnect,
330 | AuthUipcConnect,
331 | AuthExchangedata,
332 | AuthSetacl,
333 | NotifySetacl,
334 | NotifyPtyGrant,
335 | NotifyPtyClose,
336 | AuthProcCheck,
337 | NotifyProcCheck,
338 | AuthGetTask,
339 | AuthSearchfs,
340 | NotifySearchfs,
341 | AuthFcntl,
342 | AuthIokitOpen,
343 | AuthProcSuspendResume,
344 | NotifyProcSuspendResume,
345 | NotifyCsInvalidated,
346 | NotifyGetTaskName,
347 | NotifyTrace,
348 | NotifyRemoteThreadCreate,
349 | AuthRemount,
350 | NotifyRemount,
351 | Last,*/
352 | }
353 |
354 | #[derive(Clone, Debug, Serialize, Deserialize)]
355 | pub enum EsCacheResult {
356 | Yes,
357 | No,
358 | }
359 |
360 | #[derive(Clone, Debug, Serialize, Deserialize)]
361 | pub enum EsActionType {
362 | Auth,
363 | Notify,
364 | }
365 |
366 | #[derive(Clone, Debug, Serialize, Deserialize)]
367 | pub enum EsAuthResult {
368 | Allow,
369 | Deny,
370 | }
371 |
372 | #[derive(Clone, Debug, Serialize, Deserialize)]
373 | pub enum EsResultType {
374 | Auth,
375 | Flags,
376 | }
377 |
378 | #[repr(C)]
379 | #[derive(Debug, Copy, Clone, Serialize, Deserialize)]
380 | pub struct EsEventId {
381 | pub reserved: [u8; 32usize],
382 | }
383 |
384 | #[derive(Clone, Debug, Serialize, Deserialize)]
385 | pub struct EsResultNotifyResult {
386 | pub flags: u32,
387 | }
388 |
389 | #[derive(Clone, Debug, Serialize, Deserialize)]
390 | pub struct EsResult {
391 | pub result_type: EsResultType,
392 | pub result: EsResultNotifyResult,
393 | }
394 |
395 | #[derive(Clone, Debug, Serialize, Deserialize)]
396 | pub enum EsAction {
397 | Auth(EsEventId),
398 | Notify(EsResult),
399 | }
400 |
401 | // This is only needed because EsMessage contains a raw pointer
402 | // to the es_message
403 | unsafe impl Send for EsMessage {}
404 | unsafe impl Sync for EsMessage {}
405 | #[derive(Clone, Debug, Serialize)]
406 | pub struct EsMessage {
407 | pub version: u32,
408 | pub time: u64,
409 | pub mach_time: u64,
410 | pub deadline: u64,
411 | pub process: EsProcess,
412 | pub seq_num: u64,
413 | pub action: EsAction,
414 | pub action_type: EsActionType,
415 | pub event: EsEvent,
416 | #[serde(skip_serializing)]
417 | raw_message: *const es_message_t,
418 | }
419 |
420 |
421 | struct EsClientHidden {
422 | client: *mut es_client_t,
423 | active_subscriptions: HashSet,
424 | }
425 |
426 | // Unfortunately this system is a little over zealous
427 | // because it means we have to lock even to read active subscriptions.
428 | // Optimize this later if it provides too much contention with responding
429 | // to messages.
430 | #[derive(Clone)]
431 | pub struct EsClient {
432 | client: Arc>,
433 | }
434 |
435 | // TODO: Codegen these in the future
436 | // TODO: Really. Codegen these in the future along with the protobuf defintions
437 | pub fn raw_event_to_supportedesevent(event_type: u64) -> Option {
438 | Some(match event_type {
439 | 0 => SupportedEsEvent::AuthExec,
440 | 1 => SupportedEsEvent::AuthOpen,
441 | 2 => SupportedEsEvent::AuthKextload,
442 | 6 => SupportedEsEvent::AuthRename,
443 | 7 => SupportedEsEvent::AuthSignal,
444 | 8 => SupportedEsEvent::AuthUnlink,
445 | 9 => SupportedEsEvent::NotifyExec,
446 | 10 => SupportedEsEvent::NotifyOpen,
447 | 11 => SupportedEsEvent::NotifyFork,
448 | 12 => SupportedEsEvent::NotifyClose,
449 | 17 => SupportedEsEvent::NotifyKextload,
450 | 18 => SupportedEsEvent::NotifyKextunload,
451 | 19 => SupportedEsEvent::NotifyLink,
452 | 25 => SupportedEsEvent::NotifyRename,
453 | 31 => SupportedEsEvent::NotifySignal,
454 | 32 => SupportedEsEvent::NotifyUnlink,
455 | 42 => SupportedEsEvent::AuthLink,
456 | 67 => SupportedEsEvent::AuthReadDir,
457 | 68 => SupportedEsEvent::NotifyReadDir,
458 | _ => return None
459 | })
460 | }
461 |
462 | // TODO: Really. Codegen these in the future along with the protobuf defintions
463 | pub fn supportedesevent_to_raw_event(event_type: &SupportedEsEvent) -> u32 {
464 | match event_type {
465 | SupportedEsEvent::AuthExec => 0,
466 | SupportedEsEvent::AuthOpen => 1,
467 | SupportedEsEvent::AuthKextload => 2,
468 | SupportedEsEvent::AuthRename => 6,
469 | SupportedEsEvent::AuthSignal => 7,
470 | SupportedEsEvent::AuthUnlink => 8,
471 | SupportedEsEvent::NotifyExec => 9,
472 | SupportedEsEvent::NotifyOpen => 10,
473 | SupportedEsEvent::NotifyFork => 11,
474 | SupportedEsEvent::NotifyClose => 12,
475 | SupportedEsEvent::NotifyKextload => 17,
476 | SupportedEsEvent::NotifyKextunload => 18,
477 | SupportedEsEvent::NotifyLink => 19,
478 | SupportedEsEvent::NotifyRename => 25,
479 | SupportedEsEvent::NotifySignal => 31,
480 | SupportedEsEvent::NotifyUnlink => 32,
481 | SupportedEsEvent::AuthLink => 42,
482 | SupportedEsEvent::AuthReadDir => 67,
483 | SupportedEsEvent::NotifyReadDir => 68,
484 | }
485 | }
486 |
487 | fn es_notify_callback(_client: *mut es_client_t, message: *mut es_message_t, tx: Sender) {
488 | let message = match parse_es_message(message) {
489 | Err(e) => { println!("Could not parse message: {}", e); return},
490 | Ok(x) => x,
491 | };
492 |
493 | match tx.send(message) {
494 | Err(e) => println!("Error logging event: {}", e),
495 | _ => (),
496 | }
497 | }
498 |
499 | pub fn create_es_client(tx: Sender) -> Result {
500 | let mut client: *mut es_client_t = std::ptr::null_mut();
501 | let client_ptr: *mut *mut es_client_t = &mut client;
502 |
503 | let handler = ConcreteBlock::new(move |client: *mut es_client_t, message: *mut es_message_t| {
504 | es_notify_callback(client, message, tx.clone());
505 | }).copy();
506 |
507 | match unsafe { es_new_client(client_ptr, &*handler as *const Block<_, _> as *const std::ffi::c_void) } {
508 | ES_NEW_CLIENT_SUCCESS => {
509 | let hidden = EsClientHidden {
510 | client: client,
511 | active_subscriptions: HashSet::new(),
512 | };
513 | Ok(EsClient {
514 | client: Arc::new(Mutex::new(hidden)),
515 | }
516 | )},
517 | ES_NEW_CLIENT_ERROR_INVALID_ARGUMENT => Err(EsClientError{
518 | details: String::from("Something incorrect was passed to es_new_client"),
519 | error_type: EsNewClientResult::ErrorInvalidArgument,
520 | }),
521 | ES_NEW_CLIENT_ERROR_INTERNAL => Err(EsClientError{
522 | details: String::from("es_new_client experienced an internal error"),
523 | error_type: EsNewClientResult::ErrorInternal,
524 | }),
525 | ES_NEW_CLIENT_ERROR_NOT_ENTITLED => Err(EsClientError{
526 | details: String::from("This process doesn't have the EndpointSecurity entitlement. (Is the binary signed correctly, is there a provisioning profile installed to allow your program to access EPS?)"),
527 | error_type: EsNewClientResult::ErrorNotEntitled,
528 | }),
529 | ES_NEW_CLIENT_ERROR_NOT_PERMITTED => Err(EsClientError{
530 | details: String::from("This process is not permitted to use the EndpointSecurity Framework. (Do you have Full Disk Access for your process?)"),
531 | error_type: EsNewClientResult::ErrorNotPermitted,
532 | }),
533 | ES_NEW_CLIENT_ERROR_NOT_PRIVILEGED => Err(EsClientError{
534 | details: String::from("The process must be running as root to access the EndpointSecurity Framework"),
535 | error_type: EsNewClientResult::ErrorNotPrivileged,
536 | }),
537 | ES_NEW_CLIENT_ERROR_TOO_MANY_CLIENTS => Err(EsClientError{
538 | details: String::from("There are too many clients connected to EndpointSecurit"),
539 | error_type: EsNewClientResult::ErrorTooManyClients,
540 | }),
541 | _ => Err(EsClientError{
542 | details: String::from("es_new_client returned an unknown error"),
543 | error_type: EsNewClientResult::Unknown,
544 | }),
545 | }
546 | }
547 |
548 | // This might not be true. I'm talking with Apple to figure it out but nothing
549 | // seems to have broken with it yet.
550 | // @obelisk Investigate more
551 | unsafe impl Send for EsClient {}
552 | unsafe impl Sync for EsClient {}
553 |
554 | impl EsClient {
555 | // Clear the cache of decisions. This should be done sparringly as it affects ALL
556 | // client for the entire system. Doing this too frequently will impact system performace
557 | pub fn clear_cache(&self) -> Result <(), ClearCacheResult> {
558 | let client = (*self.client).lock();
559 | let client = match client {
560 | Ok(c) => c,
561 | Err(e) => {
562 | error!("Could not acquire lock for client: {}", e);
563 | return Err(ClearCacheResult::ErrorInternal)
564 | },
565 | };
566 |
567 | let response = unsafe {
568 | es_clear_cache(client.client)
569 | };
570 |
571 | match response {
572 | ES_CLEAR_CACHE_RESULT_SUCCESS => Ok(()),
573 | ES_CLEAR_CACHE_RESULT_ERR_INTERNAL => Err(ClearCacheResult::ErrorInternal),
574 | ES_CLEAR_CACHE_RESULT_ERR_THROTTLE => Err(ClearCacheResult::ErrorThrottle),
575 | _ => {
576 | error!("Unknown response from es_clear_cache. Perhaps the library needs updating?");
577 | Err(ClearCacheResult::ErrorInternal)
578 | }
579 | }
580 | }
581 |
582 | // Start receiving callbacks for specified events
583 | pub fn subscribe_to_events(&self, events: &Vec) -> bool {
584 | if events.len() > 128 {
585 | println!("Too many events to subscribe to!");
586 | return false;
587 | }
588 |
589 | let client = (*self.client).lock();
590 | let mut client = match client {
591 | Ok(c) => c,
592 | Err(_) => return false,
593 | };
594 |
595 | let events:Vec<&SupportedEsEvent> = events.iter().filter(|x| !client.active_subscriptions.contains(x)).collect();
596 | if events.len() == 0 {
597 | debug!(target: "endpointsecurity-rs", "No new events being subscribed to");
598 | return true;
599 | }
600 |
601 | let mut c_events: [u32; 128] = [0; 128];
602 | let mut i = 0;
603 | for event in &events {
604 | c_events[i] = supportedesevent_to_raw_event(&*event);
605 | i += 1;
606 | }
607 |
608 | unsafe {
609 | match es_subscribe(client.client, &c_events as *const u32, events.len() as u32) {
610 | ES_RETURN_SUCCESS => {
611 | for event in events {
612 | client.active_subscriptions.insert(*event);
613 | }
614 | true
615 | },
616 | _ => false,
617 | }
618 | }
619 | }
620 |
621 | // Unsubscribe from events and stop receiving callbacks for them
622 | pub fn unsubscribe_to_events(&self, events: &Vec) -> bool {
623 | if events.len() > 128 {
624 | println!("Too many events to unsubscribe to!");
625 | return false;
626 | }
627 |
628 | let client = (*self.client).lock();
629 | let mut client = match client {
630 | Ok(c) => c,
631 | Err(_) => return false,
632 | };
633 |
634 | let events:Vec<&SupportedEsEvent> = events.iter().filter(|x| client.active_subscriptions.contains(x)).collect();
635 | if events.len() == 0 {
636 | debug!(target: "endpointsecurity-rs", "Not subscribed to any events request to unsubscribe from");
637 | return true;
638 | }
639 |
640 | let mut c_events: [u32; 128] = [0; 128];
641 | let mut i = 0;
642 | for event in &events {
643 | c_events[i] = supportedesevent_to_raw_event(&*event);
644 | i += 1;
645 | }
646 |
647 | unsafe {
648 | match es_unsubscribe(client.client, &c_events as *const u32, events.len() as u32) {
649 | ES_RETURN_SUCCESS => {
650 | for event in events {
651 | client.active_subscriptions.remove(event);
652 | }
653 | true
654 | },
655 | _ => false,
656 | }
657 | }
658 | }
659 |
660 | // Set your subscriptions to these regardless of what they were before
661 | pub fn set_subscriptions_to(&self, events: &Vec) -> bool {
662 | if events.len() > 128 {
663 | println!("Too many events to unsubscribe to!");
664 | return false;
665 | }
666 | let new_subscriptions:Vec;
667 | let remove_subscriptions:Vec;
668 |
669 | {
670 | let client = (*self.client).lock();
671 | let client = match client {
672 | Ok(c) => c,
673 | Err(_) => return false,
674 | };
675 |
676 | // Filter out all subscriptions that we already have
677 | new_subscriptions = events.iter().filter(|x| !client.active_subscriptions.contains(x)).copied().collect();
678 |
679 | // For all subscriptions we have, keep them in this remove list if they are not in our new list
680 | remove_subscriptions = client.active_subscriptions.iter().filter(|x| !events.contains(x)).copied().collect();
681 | if !new_subscriptions.is_empty() {
682 | info!(target: "endpointsecurity-rs", "Adding subscriptions for: {}",
683 | new_subscriptions.iter().fold(String::from(""), |acc, x| acc + &x.to_string() + ", "));
684 | }
685 |
686 | if !remove_subscriptions.is_empty() {
687 | info!(target: "endpointsecurity-rs", "Removing subscriptions for: {}",
688 | remove_subscriptions.iter().fold(String::from(""), |acc, x| acc + &x.to_string() + ", "));
689 | }
690 | }
691 | self.unsubscribe_to_events(&remove_subscriptions) && self.subscribe_to_events(&new_subscriptions)
692 | }
693 |
694 | pub fn respond_to_flags_event(&self, message: &EsMessage, authorized_flags: u32, should_cache: &EsCacheResult) -> EsRespondResult {
695 | let cache = match should_cache {
696 | EsCacheResult::Yes => true,
697 | EsCacheResult::No => false,
698 | };
699 |
700 | let client = (*self.client).lock();
701 | let client = match client {
702 | Ok(c) => c,
703 | Err(_) => return EsRespondResult::UnknownResponse, // TODO Fix this
704 | };
705 |
706 | match unsafe { es_respond_flags_result(client.client, message.raw_message, authorized_flags, cache) } {
707 | ES_RESPOND_RESULT_SUCCESS => EsRespondResult::Sucess,
708 | ES_RESPONSE_RESULT_ERROR_INVALID_ARGUMENT => EsRespondResult::ErrorInvalidArgument,
709 | ES_RESPOND_RESULT_ERROR_INTERNAL => EsRespondResult::ErrorInternal,
710 | ES_RESPOND_RESULT_NOT_FOUND => EsRespondResult::NotFound,
711 | ES_RESPOND_RESULT_ERROR_DUPLICATE_RESPONSE => EsRespondResult::ErrorDuplicateResponse,
712 | ES_RESPONSE_RESULT_ERROR_EVENT_TYPE => EsRespondResult::ErrorEventType,
713 | _ => EsRespondResult::UnknownResponse,
714 | }
715 | }
716 |
717 | pub fn respond_to_auth_event(&self, message: &EsMessage, response: &EsAuthResult, should_cache: &EsCacheResult) -> EsRespondResult {
718 | let cache = match should_cache {
719 | EsCacheResult::Yes => true,
720 | EsCacheResult::No => false,
721 | };
722 |
723 | let response = match response {
724 | EsAuthResult::Allow => ES_AUTH_RESULT_ALLOW,
725 | EsAuthResult::Deny => ES_AUTH_RESULT_DENY,
726 | };
727 |
728 | let client = (*self.client).lock();
729 | let client = match client {
730 | Ok(c) => c,
731 | Err(_) => return EsRespondResult::UnknownResponse, // TODO Fix this
732 | };
733 |
734 | match unsafe { es_respond_auth_result(client.client, message.raw_message, response, cache) } {
735 | ES_RESPOND_RESULT_SUCCESS => EsRespondResult::Sucess,
736 | ES_RESPONSE_RESULT_ERROR_INVALID_ARGUMENT => EsRespondResult::ErrorInvalidArgument,
737 | ES_RESPOND_RESULT_ERROR_INTERNAL => EsRespondResult::ErrorInternal,
738 | ES_RESPOND_RESULT_NOT_FOUND => EsRespondResult::NotFound,
739 | ES_RESPOND_RESULT_ERROR_DUPLICATE_RESPONSE => EsRespondResult::ErrorDuplicateResponse,
740 | ES_RESPONSE_RESULT_ERROR_EVENT_TYPE => EsRespondResult::ErrorEventType,
741 | _ => EsRespondResult::UnknownResponse,
742 | }
743 | }
744 | }
745 |
746 | impl Drop for EsClient {
747 | fn drop(&mut self) {
748 | unsafe {
749 | let client = (*self.client).lock();
750 | let mut client = match client {
751 | Ok(c) => c,
752 | Err(_) => return (),
753 | };
754 |
755 | es_delete_client(client.client);
756 | // Probably unnecessary
757 | client.active_subscriptions.clear();
758 | }
759 | }
760 | }
--------------------------------------------------------------------------------