I believe this is a general problem when writing low-level security and monitoring tools.
The EndpointSecurity framework provides lots of information for the events it allows to Monitor (and/or Authorize) but all these are "kernel level" details, that usually don't allow understanding (and aggregating) low-level events according to User Intent
e.g. Saving all attachments of an e-mail message from Apple Mail program to a USB-disk, will create a long series of low-level file-system events, some preliminary (touching and verifying the destination can fit the attachments) some for moving/copying, some for rounding-up, some for indexing those files on the new volume, and so on.
Is it possible to somehow obtain the NSUserActivity object/instance/Identifier related to such low-level event? Is the kernel itself aware of user activities?
Any hint or suggestion or strategy will be greatly appreciated
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi, I have a LaunchAgent (whose .plist is in /Library/LaunchAgents/ as expected)
It is build as a single un-bundled binary, and installed away from regular user access at /Library/MyCompany/executables/
When I try to init a reference to UNUserNotificationCenter:
UNUserNotificationCenter *unc = [UNUserNotificationCenter currentNotificationCenter];
I immediately have an exception, like thus:
2021-05-02 19:57:39.297823+0300 agent[12504:225879] *** Assertion failure in +[UNUserNotificationCenter currentNotificationCenter], /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/UserNotifications/UserNotifications-281.6/UNUserNotificationCenter.m:44
2021-05-02 19:57:39.299464+0300 agent[12504:225879] [General] An uncaught exception was raised
2021-05-02 19:57:39.299518+0300 agent[12504:225879] [General] bundleProxyForCurrentProcess is nil: mainBundle.bundleURL file:///Users/mshneor/Library/Developer/Xcode/DerivedData/agent-dgvdlbtivmsjdgdkhhjwaxgrkyht/Build/Products/Debug/
Obviously the problem is bundleProxyForCurrentProcess is nil: mainBundle.bundleURL
Now... I could try to bundle my agent somehow, but that's a bit awkward, and I don't know how - it's not an App, and It's not a part of any App, and I don't know about a special "Code Bundle" type (like plugins etc.) that fits a LaunchAgent. Moreover - it will never be "launched" by it's info.plist in the bundle - only via the .plist in /Library/LaunchAgents by launchd.
So... is it possible to work around this? Could I somehow tell my Agent's process "Here is our main-bundle" at runtime, and provide a special bundle the agent is not embedded in?
what does UNUserNotificationCenter need from main bundle? maybe I can supply this programmatically in advance?
Any hint will be greatly appreciated.
I have a global daemon managed by launchd, whose .plist is installed in /Library/LaunchDaemons).
To be correctly entitled and code-signed so it can communicate with EndpointSecurity framework, its executable resides in a normal Mac App bundle (main() will run as minimal UI when launched from UI, and as a daemon when launched by launchd).
This means that the ProgramArguments.0 in its .plist looks something like
/Library/PrivilegedHelperTools/MyDaemonApp.app/Contents/MacOS/MyDaemonApp
Now I need this daemon to publish an XPC Service (with few control commands) so that other components of our system (UI app, a user-context launchd-daemon and another launchd global-daemon) will be able to connect to The XPC Service and control it via the published protocol.
I read some answers here, and also found a working order sample code that does just this here - https://github.com/jdspoone/SampleOSXLaunchDaemon
But when I apply its content to my global daemon, word for word - it doesn't work - meaning, clients cannot create a connection to The XPC Service.
The daemon is up and running, and functional. its .plist is quite simple and looks like this:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.mycompany.itm.service</string>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>MachServices</key>
<dict>
<key>com.mycompany.itm.service</key>
<true/>
</dict>
<key>ProgramArguments</key>
<array>
<string>/Library/PrivilegedHelperTools/IMyDaemonApp.app/Contents/MacOS/MyDaemonApp</string>
<string>-monitor</string>
<string>-protectDeviceProtocol</string>
<string>USB</string>
</array>
</dict>
</plist>
It creates and starts an XPC listener in MYXPCListener.h like thus:
#import <Foundation/Foundation.h>
#import "MYXPCProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface OITPreventionXPCService : NSObject (instancetype) init;
(void) start; /* Begin listening for incoming XPC connections */
(void) stop; /* Stop listening for incoming XPC connections */
@end
NS_ASSUME_NONNULL_END
and the implementation is:
/* AppDelegate.m */
@interface MYXPCService () <NSXPCListenerDelegate, OITPreventionXPCProtocol>
@property (nonatomic, strong, readwrite) NSXPCListener *listener;
@property (nonatomic, readwrite) BOOL started;
@end
@implementation OITPreventionXPCService (instancetype) init {
if ((self = [super init]) != nil) {
_listener = [[NSXPCListener alloc] initWithMachServiceName:@"com.mycompany.itm.service"];
_listener.delegate = self;
if (_listener == nil) {
os_log_error(myLog, "XPCListener failed to initialize");
}
_started = NO;
}
return self;
} (void) start {
assert(_started == NO);
[_listener resume];
os_log_info(myLog, "XPCListener resumed");
_started = YES;
} (void) stop {
assert(_started == YES);
[_listener suspend];
os_log_info(myLog, "XPCListener suspended");
_started = NO;
}
/* NSXPCListenerDelegate implementation */
(BOOL) listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
os_log_info(myLog, "Prevention XPCListener is bequsted a new connection");
assert(listener == _listener);
assert(newConnection != nil);
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MYXPCProtocol)];
newConnection.exportedObject = self;
		[newConnection resume];
return YES;
}
/* Further down this implementation, I have implementations to all the methods in MYXPCProtocol. */
@end
Now the client code (and I tried EVERY kind of client, signed unsigned, daemon, UI, root privileged, or user-scoped - whatever). For example, in the AppDelegate of a UI app:
#import "AppDelegate.h"
#import "MYXPCProtocol.h"
@interface AppDelegate ()
@property (strong) IBOutlet NSWindow *window;
@property (nonatomic, strong, readwrite) NSXPCConnection *connection; /* lazy initialized */
@end
@implementation AppDelegate (NSXPCConnection *) connection
{
if (_connection == nil) {
_connection = [[NSXPCConnection alloc] initWithMachServiceName:daemonLabel options:NSXPCConnectionPrivileged];
_connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MYXPCProtocol)];
_connection.invalidationHandler = ^{
self->_connection = nil;
NSLog(@"connection has been invalidated");
};
[_connection resume]; /* New connections always start suspended */
}
return _connection;
}
(IBAction) getServiceStatus:(id)sender
{
[self.connection.remoteObjectProxy getStatus:^(NSString * _Nonnull status) {
NSLog(@"MY XPC Service status is: %@", status);
}];
}
@end
but no matter what I do - I always get the "connection invalidated".
The sample launchDaemon that works - is not code-signed at all!!! but mine, which is both signed and checking of which yields
$ spctl --assess --verbose IMyDaemonApp.app
IMyDaemonApp.app: accepted
source=Notarized Developer ID
I'm at a loss - and would like to get any advice, or any helpful documentation (I've been reading TN2083, and man launchctl and man launchd.plist and many other pages - to no avail. There seems to be no real "programming guide" for XPC and no reasonable sample code on Apple developer site to fit my needs.
Last - this is MacOS 10.15.7, and latest Xcode 12.3
My new EndpointSecurity client code receives event messages of type esmessaget. These contain 3 time values.
struct timespec time;
uint64_t mach_time;
uint64_t deadline;
To interpret the deadline I refer to the mach_time. However, when I wish to log/formet/otherwise-consider the "wall time" where the event was created - I need to translate the timespec into NSDate (hmm... NSTimeInterval?) and I fail to find any documentation or hint about the right way to do so.
What I tried so far looks like this:
NSDate * timestamp = [NSDate dateWithTimeIntervalSince1970:(double)(message->time.tv_sec) + (double)(message->time.tv_sec) / 1E9 ];
which at least mathematically seems reasonable, but I'm not sure this is the right answer, and I don't know anything about the behaviour of timespec, its origin and accuracy, and whether or not it is consistent.
Can anyone shed a little light on the relation between these two time formats?
Thanks!
Topic:
App & System Services
SubTopic:
Drivers
Tags:
System Extensions
Endpoint Security
Objective-C
My agent/service relies on Accessibility APIs. Being installed by IT on all corporate Macs, it receives its permission to use these APIs via a configuration profile installed on the Mac, and not via the System-Preferences Security & Privacy panel (Privacy tab, Accessibility item).
Problem is - if that profile is removed, or changed to remove this permission - The agent currently has no way to know it, and will hang on the next call to some AX API.
our code calls
@result Returns TRUE if the current process is a trusted accessibility client, FALSE if it is not.
*/
extern Boolean AXIsProcessTrustedWithOptions (CFDictionaryRef __nullable options) CF_AVAILABLE_MAC(10_9);
before using other AX APIs, but sadly - the method returns true even when accessibility has been denied by removing the profile.
By contrast - if If user manually un-checks the Accessibility for this agent in the System-Preferences panel - the function returns false immediately.
If after removing the profile, I kill my agent (launchd then relaunches it) - then calling AXIsProcessTrustedWithOptions returns false as expected.
This seems to be a bug of some kind or incomplete behaviour, but I need a workaround as soon as possible.
My first "wish" would be to be able to register for and receive some system-wide NSNotification about "configuration profile changes", at which time, I could decide to exit my agent, and relaunch with accessibility permissions synchronised.
Or is there any AX internal notification I could register for? haven't found any.
Any clue would be greatly appreciated.
Topic:
Developer Tools & Services
SubTopic:
General
Tags:
Enterprise
Notification Center
Accessibility