XPC as plain IPC, no launchd involved

Hi there. I guess the answer should be obvious and I'm just blind not to see it in the docs and sample code, but find it, alas, I failed.


How does one use XPC just for IPC, without exploiting XPC Services? As Quinn points out in


See the answer in context


you can use XPC in other scenarios; and the one of mine is really very plain: I just have got an application, which should behave as an XPC server; and a command-line executable (it happens to be a Mozilla NMH, but I believe that's rather irrelevant), which — when launched — should connect to the app and communicate with it. Normally, I would use Distributed Objects for that; nevertheless, this time security is important, and thus I would prefer to exploit the modern and secure communication channel, which allows me to make sure that the client tool signature checks, etc.


Based on the documentation and code samples, I have tried at the server side


-(void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    self.listener=[[NSXPCListener alloc] initWithMachServiceName:MyXPCName];
    self.listener.delegate=self;
    [self.listener resume];
}
-(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
    ... ... unimportant, for it never gets called ... ...


and at the client side


NSXPCConnection *proxy=[[NSXPCConnection alloc] initWithMachServiceName:MyXPCName options:0];
proxy.remoteObjectInterface=[NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)];
proxy.invalidationHandler = ^{ NSLog(@"### Invalidated"); };
proxy.interruptionHandler=^{ NSLog(@"### Interrupted"); };
[proxy resume];
id server=[proxy remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
    NSLog(@"### Error: %@",error);
}];
[server testService2WithReply:^(NSString *reply) { NSLog(@"Got answer (%@)",reply); }]; // part of MyXPCProtocol


but it does not seem to work: my server's listener:shouldAcceptNewConnection: method gets never called, and the client — regardless whether the server runs or not! — invalidates the connexion as soon as I try to send the testService2WithReply: message.


It sort of looks like the problem might be at the server side which probably does not publish the listener name properly, for when I try to NSLog(@"%@",self.listener), I get something like “<NSXPCListener: 0x7fedb974c990> service: (null)” (contrariwise, at the client side, logging out the proxy shows something like “<NSXPCConnection: 0x100500eb0> connection to service named cz.ocs.XPCT1.test”).


My server application Info.plist contains


<key>XPCService</key>
<dict>
  <key>JoinExistingSession</key>
  <true/>
  <key>ServiceType</key>
  <string>Application</string>
</dict>


(I have tried also without JoinExistingSession, in vain.) Both the application and client are properly signed, codesign confirms that both Designated Requirements are satisfied and both the TeamIdentifiers match. The server application is not sandboxed.


Would be very glad if someone can point me to a proper documentation or code sample how to do this. All docs and code samples I managed to find explained how to use properly the launchd-based XPC Services, which I do not need here at all. Thanks a lot!

Answered by DTS Engineer in 314136022

How does one use XPC just for IPC, without exploiting XPC Services?

One does not. XPC connections are always mediated via

launchd
.

I just have got an application, which should behave as an XPC server; and a command-line executable … which … should connect to the app and communicate with it.

This arrangement isn’t supported by XPC because it runs it’s a layering inversion. Applications use services, they don’t provide them. XPC does support XPC Services, which allows you to break up your app into components that provide services (the XPC Services) and components that use them (everything else), but this doesn’t work well for your scenario due to namespace issues. Specifically,

launchd
sets up a per-application namespace where it registers each XPC Service in that app, but your command line tool is running outside of that namespace.

I only know of one way for an app to provide an XPC service (note the lower case) that’s visible to a command line tool, and that’s via an XPC login item (as illustrated by the AppSandboxLoginItemXPCDemo sample code). Due to the way these are implemented the XPC service provided by the login item is published to the entire GUI session.

Clearly this is a bit of a hack, and I encourage you to file an enhancement request for the features you need. Be aware, however, that a request to allow an app to publish an arbitrary XPC service is likely to be rejected. You could, however, imagine a way for an app to include an XPC Service with a wider visibility, possibly with code signing requirements imposed on the clients of such a service.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"


WWDC runs Mon, 4 Jun through to Fri, 8 Jun. During that time all of DTS will be at the conference, helping folks out face-to-face.
Accepted Answer

How does one use XPC just for IPC, without exploiting XPC Services?

One does not. XPC connections are always mediated via

launchd
.

I just have got an application, which should behave as an XPC server; and a command-line executable … which … should connect to the app and communicate with it.

This arrangement isn’t supported by XPC because it runs it’s a layering inversion. Applications use services, they don’t provide them. XPC does support XPC Services, which allows you to break up your app into components that provide services (the XPC Services) and components that use them (everything else), but this doesn’t work well for your scenario due to namespace issues. Specifically,

launchd
sets up a per-application namespace where it registers each XPC Service in that app, but your command line tool is running outside of that namespace.

I only know of one way for an app to provide an XPC service (note the lower case) that’s visible to a command line tool, and that’s via an XPC login item (as illustrated by the AppSandboxLoginItemXPCDemo sample code). Due to the way these are implemented the XPC service provided by the login item is published to the entire GUI session.

Clearly this is a bit of a hack, and I encourage you to file an enhancement request for the features you need. Be aware, however, that a request to allow an app to publish an arbitrary XPC service is likely to be rejected. You could, however, imagine a way for an app to include an XPC Service with a wider visibility, possibly with code signing requirements imposed on the clients of such a service.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"


WWDC runs Mon, 4 Jun through to Fri, 8 Jun. During that time all of DTS will be at the conference, helping folks out face-to-face.

Aha, thanks, I see.


Given I need to solve the problem now (not when an ehancement request might perhaps be approved in future), what communication API would you recommend, given the scenario above (an application which offers services, a CLI tool which is a client, as secure as reasonably possible)? The XPC login item hack? Or another API altogether?


So far I have used Distributed Objects in such cases and it worked well, but I understand they are vulnerable to number of attacks and they do not offer an API to check the client's signature, etc.


What would you use?


Thanks a lot,

OC

Incidentally — I am playing with the login item to test the hack, and I have found that there's some pretty convenient trickery, probably at lldb level:

  • if my login item is launched properly (i.e., through SMLoginItemSetEnabled), it works as expected;
  • if my login item is launched manually (e.g., by running its executable from Terminal), it does not work, the service is invalidated — fully as expected, of course;
  • if my login item is run by Xcode (or manually in lldb) though, the XPC communication does work!


That's a really nice surprise; I have expected to debug, I would have to launch the login item through SMLoginItemSetEnabled, and to connect lldb to its pid. Kudos to whomever designed/implemented this little trick!

Ah, there's one more question: having played with the thing for awhile, I have found that at the client side, the xpcconnection.processIdentifier seems to be always zero.


Do please correct me if I am wrong, but I believe with the login item hack, the scenario in which the attacker would replace my login item by his own, and my CLI tool would connect to the fake server, is plausible (though I admit I did not test it myself).


Is there any way for the XPC client to check the server's signature?


Thanks a lot again,

OC

XPC as plain IPC, no launchd involved
 
 
Q