Post

Replies

Boosts

Views

Activity

Reply to Can my User-Agent know (be notified) of Configuration profile changes?
No one cared to answer, and I don't have an answer either - but in the mean time I received confirmation from Apple (I opened a tech-support-incident for this issue) that indeed this is a "Bug" or "unexpected behavior" of the operating system. No workaround was suggested by the tech-support engineers. My rather horrible work-around to this scenario is to "poll" by spawning a new instance of the same daemon executable (using NSTask) every few minutes, just so it can call the AXIsProcessTrustedWithOptions method, get a "fresh" and reliable answer and exit with return-code. If that answer is "NO", then I quit+relaunch the original process.
Dec ’20
Reply to XPC Services for a daemon
Have you found the solution? can you advise on how to do this? I'm trying this for days and days now, with very little success --- I have a sample code (from GitHub) that does this, but its daemon is not code-signed at all. When I try to apply this to my own global daemon - the client can't connect to it - its connection is invalidated, and I simply don't know what's wrong and what to don next. So can you spare your findings here, if you finally got it to work?
Jan ’21
Reply to How to publish an XPC Service in a global daemon that employs EndpointSecurity framework?
My daemon is up and running at all times, and is doing its work (some kind of monitoring and blocking of specific authorization events from EndpointSecurity framework). I started this endeavor with a working-order global daemon. My goal is to keep it small fast and safe, but still receive "control commands" from external process, and for that I want it to publish an XPC Service. Of course it does appear in the sudo launchctl list, and I can also launchctl print its state and properties without problem.
Topic: Code Signing SubTopic: Entitlements Tags:
Jan ’21
Reply to How to publish an XPC Service in a global daemon that employs EndpointSecurity framework?
I saved and compared the output of launchctl print for both the working sample global-daemon and mine. They both look benign and identical, except for the expected differences (label of the daemon, path of executable, PID and port number of the endpoint). They also both have this environment = { XPC_SERVICE_NAME => com.mycompany.itm.service } entry, which says launchd KNOWS my global daemon should publish an XPC Service by this name. I managed to make another tiny progress, by trying to connect to my global daemon from the UI app of the sample-code I'm using - the one that succeeds in communicating with the sample daemon. I duplicated the code there, and try to talk to the two daemons - each with its own label and protocol. Now, the UI client app no longer calls my connection invalidation handler immediately as I try to get to the remote object proxy and send it a message, like thus:     id ro = [self.myServiceConnection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {         NSLog(@"Error obtaining remote object: %@", error);  }];     if (myServiceRemote) {         [preventionRemote getStatus: ^(NSString * _Nonnull status) {  NSLog(@"Service status: %@", status);  }];     } I get this NSError. 2021-01-17 20:20:38.895040+0200 Client[18858:420994] Error obtaining remote object: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service on pid 0 named com.mycompany.itm.service" UserInfo={NSDebugDescription=connection to service on pid 0 named com.mycompany.itm.service} Now what? I can't figure out how to debug this thing effectively - these are caveman's methods...
Topic: Code Signing SubTopic: Entitlements Tags:
Jan ’21
Reply to How to publish an XPC Service in a global daemon that employs EndpointSecurity framework?
Apple dev forum's usability and documentation is in par with the other parts of the OS and frameworks... that is... again I find myself disappointed. Anyway, Yet another hint about this issue (still unresolved, dear Eskimo - I would be so grateful for any ideas...) When my client is sandboxed - I get the "connection invalidation" handler called. when it's NOT sandboxed, I get the NSError 4097 trying to obtain the remoteObjectProxy from the connection. Go figure. The listener on my global daemon is never called for a new connection attemp (so my logs show) so I guess the connection attempt is blocked at the launchd level, or at some security stage - however, I just can't find anything in the logs, and I don't know how WHICH processes and frameworks are involved. The more I read documentation, the more I become confused - There seem to be .plist configurations For XPC Services (that are bundled as a "plugin" within an application) and whose entries may (or not?) be relevant to my case - but where and how would I stick those in my scenario? It's been almost 2 weeks of struggle already, and I don't know where to go next.
Topic: Code Signing SubTopic: Entitlements Tags:
Jan ’21
Reply to open(/var/db/DetachedSignatures) - Undefined error:0
I have the same problem (Xcode 12.3, MacOS 10.15 Catalina and 11.1 Big Sur). Our code (a security tool based on the new EndpointSecurity framework) attempts to extract some code-signing information for processes as part of logic that determines whether or not to authorize some user actions) So we execute CFDictionaryRef attributes = (bridge CFDictionaryRef)@{(bridge NSString *)kSecGuestAttributePid : [NSNumber numberWithInt:self.pid] }; SecCSFlags options = kSecCSDefaultFlags; status = SecCodeCopyGuestWithAttributes(NULL, attributes, options, &dynamicCode); and then do some verifications on the dynamicCode -- however, we're flooded with the same logs as the OP, plus, for some reason - despite the method returning success, OS hangs and kills our process immediately at that point (EndpointSecurity clients have this harsh reaction to just about anything, including any attempt to debug, or breakpoint, or run with instruments, or experience certain exceptions etc.). When run outside of the ES context - the code runs just fine, but logs are still emitted, for each execution of the SecCodeCopyGuestWithAttributes
Topic: Privacy & Security SubTopic: General Tags:
Jan ’21
Reply to NSXPCConnection from LaunchDaemon to LaunchAgent
Hi, have a similar scenario, and have been trying to do this for quite some time, to no avail (my "Agent" isn't a UI-session, but still runs as a daemon on the user's session). My global-daemon (runs in the system domain as root) receives connection from my "Agent", "Agent" can send messages to daemon successfully, and receive results. Now if you could be so kind as to space a few lines of code explaining what it means to "send the anonymous XPC listener endpoint to the daemon" and how can Daemon use this endpoint. I tried several things and they didn't work for me so far. In my case, it's not a "one time" job. Agent connects to Daemon and Daemon needs to regularly report its actions related to the Agent's user (I can have an agent for each user logged in). My current state of code (not working) is: Agent side creating connection: ObjectiveC // lazy property getter (NSXPCConnection *) myXPCConnection { // Create the XPC Connection on demand if (_myXPCConnection == nil) { _myXPCConnection = [[NSXPCConnection alloc] initWithMachServiceName:preventionServiceLabel options:NSXPCConnectionPrivileged]; _myXPCConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(myXPCProtocol)]; // Attempt to create a bi-directional connection, so to receive reports from the XPC service. _myXPCConnection.exportedInterface = NSXPCInterface interfaceWithProtocol:@protocol(OITPreventionXPCProtocol)]; _myXPCConnection.exportedObject = self; _myXPCConnection.invalidationHandler = ^{ // on invalidation we simply nullify connection reference, so it will be re-created once app tries to use it again. [[NSOperationQueue mainQueue] addOperationWithBlock:^{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" self.myXPCConnection.invalidationHandler = nil; self.myXPCConnection = nil; os_log(preventionUILog, "connection has been invalidated"); #pragma clang diagnostic pop }]; }; _myXPCConnection.interruptionHandler = ^{ // on interruption we simply nullify connection reference, so it will be re-created once app tries to use it again. [[NSOperationQueue mainQueue] addOperationWithBlock:^{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" self.myXPCConnection.interruptionHandler = nil; self.myXPCConnection = nil; os_log(preventionUILog, "connection interrupted"); #pragma clang diagnostic pop }]; }; [_myXPCConnection resume]; // New connections always crated and starts in a suspended state } return _myXPCConnection; } Daemon side: ObjectiveC (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { assert(listener == _listener); assert(newConnection != nil); BOOL accept = NO; do { // Verify client identity, only accept if client is code-signed by our teamID and if its signingID is of our Agent. if (/* not allowed connection.... then */) break; accept = YES; // we accept connections from processes signed by our development team (proofpoint) // now see who's calling. if (/* it is Agent */) [self.monitor agentConnected:newConnection]; // Configure incoming connection. First, set the interface that the exported object implements. newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(OITPreventionXPCProtocol)]; // Next set self to be the object the connection exports. All messages sent on the connection to this service will be sent to self to handle. The connection retains the exported object. newConnection.exportedObject = self; newConnection.invalidationHandler = ^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" [self.monitor agentDisconnectedForUser:newConnection.effectiveUserIdentifier]; #pragma clang diagnostic pop }]; }; newConnection.interruptionHandler = ^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" os_log_info(myLog, "Agent Connection interrupted."); #pragma clang diagnostic pop }]; }; os_log_info(myLog, "XPCListener accepted XPC connection from pid: %d", newConnection.processIdentifier); [newConnection resume]; // New connections always start in a suspended state, start it } while(false); return accept; // Returning YES from this method tells the system that you have accepted this connection. } and last - when Connection is established, and after Daemon receives a message call and replies to Daemon - I try to use the connection from the Daemon side like this: ObjectiveC(void)reportPrevention:(MYEventESInfo * _Nonnull)eventInfo {     uid_t user = eventInfo.processInfo.auid;     NSXPCConnection *agentConnection = [self.connectedAgents objectForKey:@(user)];     if (agentConnection != nil) {         [agentConnection.remoteObjectProxy eventHandled:[eventInfo dictionary]]; // HERE I CRASH WITH EXCEPTION     }     else {         os_log(monitoringLog, "No Agent for Event: %{public}@", eventInfo );     } } Objective-C exception : method 'eventHandled:' unknown to remoteObjectProxy. Any idea what's wrong?
Mar ’21
Reply to Notification/callback mechanism
This post is wonderful, and I'm now trying to establish my "back connection" from daemon-XPC-service back to its user-bound agents. One question though. I AM SURE I read somewhere in The XPC documentation that once established - an XPC connection is bi-directional? doesn't that mean that on some lower-level both parties (the "Daemon-XPC-Service" publishing itself via machServiceName and its (multiple) clients) can initiate "requests" on the same connection? Or maybe I got this wrong and the "bi-directional" part is only the "response" passing back for a "request" ? I'd like a clarification. In my case, NSProgress (oh, lovely NSProgress) isn't enough, and I must pass a dictionary describing some work Deamon-XPC-Service has done that is related to the user's agent (client). Because I may have more than one XPC-client (actually, one per logged-in user) The global notification isn't best choice (unless I could pack there all the data I need to pass, together with the user-ID, so that all agents would listen, and filter for their own user-id) Last - both global-daemon and user-agent can of course be killed/restarted/fail-to-launch because they lack some TCC permissions and so on - how is best to manage such scenario of several clients, each with its own normal and "back" XPC connection to the Daemon?
Mar ’21
Reply to Notification/callback mechanism
Could you clarify how "Client/Agent", after creating an anonymous listener and getting its endpoint, "uses an XPC message to send that to the service." ??? To send something to the service/daemon, Client/Agent side needs to already have established a connection? and how do you send an endpoint in an XPC message? how you obtain it on the other (Service/Daemon) side? and is the new connection-from-endpoint created on the service side unique and maintained for that specific Client/Agent? Can I have more than one?
Mar ’21
Reply to Notification/callback mechanism
OK. I got it to basically work as described (reverse connection approach). Now for the "gotcha"'s. Agent (maintains anonymous listener) connects to Daemon (mach-service-named), calls the Daemon and registers its anonymous-listener's endpoint. Daemon in turn creates the reverse-connection, and later uses it to convey information to the Agent using specific protocol and methods. If my Agent goes away (killed, quits etc.) Daemon's connection for some reason doesn't know about it (neither interruption handler nor invalidation handler on the reverse-connection are being called). Is this also the source of the issue of stacking-up of pending requests in memory? Since Daemon has the original connection (Agent to Daemon) it knows when agent connection goes away - its invalidated. Can I make a practical assumption that if my original connection was invalidated - than the reverse-connection should be treated as dead too, and needs to be re-established? Also, when using the reverse-connection, could I make use of the: (id)synchronousRemoteObjectProxyWithErrorHandler:(void (^)(NSError *error))handler API_AVAILABLE(macos(10.11), ios(9.0), watchos(2.0), tvos(9.0)); To be sure I don't stack up requests? and if so... how to use it? I do not understand what's "synchronous" here --- this is just an accessor providing me a proxy.
Mar ’21
Reply to Distributed notification no longer working
kind'a beats the purpose of sandboxing? I would guess there's some special "entitlement" you can apply to the sandboxed app to allow sending data to your App, however - you are of course aware that ANY observer can register for, and receive these notifications, together with the data you package there - so this can infringe user privacy. An XPC connection is preferable, becasues The "XPC service" can verify incoming connection requests and deny based on their code-signing and other identity. XPC calls can return a response, thus you can know that your notification passed through.
Mar ’21
Reply to EndpointSecurity Process Hangs Then Dies
I could extend the complaint to any kind of debugging / externally looking at / even "Sampling" a working EndpointSecurity client daemon/app. In my experience, it doesn't matter which kind of events I'm registering for, it doesn't matter if I respond immediately (synchronously) or in deferred block, Any attempt to "poke" into the process causes (at least here - MacOS 10.15.6) complete system hang (at least Mac UI is dead so I can't really know if anything lives at that time) for about 30-40 seconds, and then OS kills my ES client process (kill -9) and then OS is free and back to normal. For that I completely gave up any debugger use, any Instruments use, or actually any debugging tool except os_log() messages. I had that guess, more of a gut feeling, that "playing inside the authorisation scheme" OS doesn't like anyone to interfere, and is so phobic about it, that it prefers killing the thing. Again - just a feeling.
Topic: App & System Services SubTopic: Drivers Tags:
Mar ’21
Reply to EndpointSecurity ES_EVENT_TYPE_AUTH_IOKIT_OPEN and IO device data
I can add to @mdolan's answer, that the DiskArbitration framework can also be used to collect information for mount-points at mount-time (AUTH_MOUNT etc), in addition to IOKit roughly... (NSDictionary *)infoForMountPoint:(NSString *)mountPoint { CFURLRef mountPointURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)mountPoint, kCFURLPOSIXPathStyle, YES);     DADiskRef = DADiskCreateFromVolumePath(kCFAllocatorDefault, _daSession, mountPointURL);     CFDictionaryRef diskDescription = DADiskCopyDescription(diskRef);     NSMutableDictionary *mountPointInfo = [NSMutableDictionary dictionaryWithDictionary: CFBridgingRelease( diskDescription )];         // Add IOKit info we need, if available...
Topic: App & System Services SubTopic: Core OS Tags:
Mar ’21