Files
FileMonitor/Library/Source/FileMonitor.m
T
Patrick Wardle dc0b736804 library now takes user-specified events
- `start` method now takes events of interest (vs. hardcoding them)
- improved tokenization of es_string_token_t
- improved handling of `ES_EVENT_TYPE_NOTIFY_CREATE` events
2019-11-27 10:39:16 -10:00

168 lines
3.9 KiB
Objective-C

//
// FileMonitor.m
// FileMonitor
//
// Created by Patrick Wardle on 9/1/19.
// Copyright © 2019 Objective-See. All rights reserved.
//
// Inspired by https://gist.github.com/Omar-Ikram/8e6721d8e83a3da69b31d4c2612a68ba
// NOTE: requires a) root b) the 'com.apple.developer.endpoint-security.client' entitlement
#import "utilities.h"
#import "FileMonitor.h"
#import <Foundation/Foundation.h>
#import <EndpointSecurity/EndpointSecurity.h>
//endpoint client
es_client_t *endpointClient = nil;
@implementation FileMonitor
//start monitoring
// pass in events of interest, count of said events, and callback
-(BOOL)start:(es_event_type_t*)events count:(uint32_t)count callback:(FileCallbackBlock)callback
{
//flag
BOOL started = NO;
//result
es_new_client_result_t result = 0;
//sync
@synchronized (self)
{
//create client
// callback invoked on file events
result = es_new_client(&endpointClient, ^(es_client_t *client, const es_message_t *message)
{
//new file obj
File* file = nil;
//init file obj
file = [[File alloc] init:(es_message_t* _Nonnull)message];
if(nil != file)
{
//invoke user callback
callback(file);
}
});
//error?
if(ES_NEW_CLIENT_RESULT_SUCCESS != result)
{
//err msg
NSLog(@"ERROR: es_new_client() failed");
//provide more info
switch (result) {
//not entitled
case ES_NEW_CLIENT_RESULT_ERR_NOT_ENTITLED:
NSLog(@"ES_NEW_CLIENT_RESULT_ERR_NOT_ENTITLED: \"The caller is not properly entitled to connect\"");
break;
//not permitted
case ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED:
NSLog(@"ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED: \"The caller is not permitted to connect. They lack Transparency, Consent, and Control (TCC) approval form the user.\"");
break;
//not privileged
case ES_NEW_CLIENT_RESULT_ERR_NOT_PRIVILEGED:
NSLog(@"ES_NEW_CLIENT_RESULT_ERR_NOT_PRIVILEGED: \"The caller is not running as root\"");
break;
default:
break;
}
//bail
goto bail;
}
//clear cache
if(ES_CLEAR_CACHE_RESULT_SUCCESS != es_clear_cache(endpointClient))
{
//err msg
NSLog(@"ERROR: es_clear_cache() failed");
//bail
goto bail;
}
//mute self
// note: you might not want this, but for a cmdline-based filemonitor
// this ensures we don't constantly report writes to current /dev/tty
es_mute_path_literal(endpointClient, [NSProcessInfo.processInfo.arguments[0] UTF8String]);
//subscribe
if(ES_RETURN_SUCCESS != es_subscribe(endpointClient, events, count))
{
//err msg
NSLog(@"ERROR: es_subscribe() failed");
//bail
goto bail;
}
} //sync
//happy
started = YES;
bail:
return started;
}
//stop
-(BOOL)stop
{
//flag
BOOL stopped = NO;
//sync
@synchronized (self)
{
//unsubscribe & delete
if(NULL != endpointClient)
{
//unsubscribe
if(ES_RETURN_SUCCESS != es_unsubscribe_all(endpointClient))
{
//err msg
NSLog(@"ERROR: es_unsubscribe_all() failed");
//bail
goto bail;
}
//delete
if(ES_RETURN_SUCCESS != es_delete_client(endpointClient))
{
//err msg
NSLog(@"ERROR: es_delete_client() failed");
//bail
goto bail;
}
//unset
endpointClient = NULL;
//happy
stopped = YES;
}
} //sync
bail:
return stopped;
}
@end