XPC Rendezvous, com.apple.security.inherit and LaunchAgent

I’m trying to implement XPC Rendezvous like Quinn described in many awesome posts on here but I’m now at a stuck point were I just have no idea.

I want to communicate with a Safari extension via XPC and also a helper application which led me to XPC Rendezvous (https://developer.apple.com/forums/thread/715338) because a XPC Service in the Extension is scoped to the container. I then made a Command Line Target and added it like its described here (https://developer.apple.com/documentation/xcode/embedding-a-helper-tool-in-a-sandboxed-app ) and also took the xpc test code and inspiration to set up my launch agent from here (https://developer.apple.com/documentation/servicemanagement/updating_your_app_package_installer_to_use_the_new_service_management_api). This command line tool should do the management for the XPC connections because it’s not in the sandboxed container.

The tool sets up the xpc connection like in the sample code directly and not in a XPC Service added via a Target template. It exposes the Mach Service.

And that looks like its building fine after some fighting but the service just wont start - I saw it trying in console and after running it in Xcode and finally finding the crash report - it brought me there (https://developer.apple.com/forums/thread/706390)

I have Process is not in an inherited sandbox. - and thinking about it, it makes sense because I first thought its just because it ran through Xcode, but its crashing this way also as a LaunchAgent.

I mean it does make sense - there is nothing to inherit because it’s spawned by launchd - and that’s what I want isn’t it - to make the Rendezvous?

Okay I thought now removing com.apple.security.inherit brings it in its own Sandbox (its needs sandboxing) but this also crashes the process because of the sandbox. Also after adding it to the App Group. What am I missing here or what do I want to accomplish? Do I want to inherit the sandbox? I guess not the helper should have its own.

The only difference I see in comparison to SMAppServiceSampleCode is it moves the product in Copy Bundle Resources, and I have a Copy Files Phase with Destination: Executables (Like the other sample code said - and that’s looks “more correct” - and well SMAppServiceSampleCode isn’t sandboxed.

I then tried making a new Command Line Target and just added App Sandbox Capability and tried to run this fresh one - and that also crashes. This makes me think I’m just ****** somewhere but I have read now everything I could find.

I’m happy to provide any Code or crash logs but I dont know what part is really relevant here, It looks like the LaunchAgent gets installed correctly and wants to run but the sandbox is preventing me. The Bundle Identifier and XPC device name of the helper starts with my teamID (I got that from here https://developer.apple.com/forums/thread/703702)

What could I be doing wrong?

Thanks a lot!

Benjamin

Replies

After a couple more hours I suspected "Command Line Target with App Sandbox just isn't possible", at least if I need it not inheriting - I got that in console

Unable to get bundle identifier because Info.plist from code signature information has no value for kCFBundleIdentifierKey.'

I didn't think much about the Select Info.plist button and even after creating a plist with bundle identifier even though the bundle identifier for this target was set in capabilities it didn't change anything then I found this (https://developer.apple.com/forums/thread/669151) and setting CREATE_INFOPLIST_SECTION_IN_BINARY made it work - for anyone for future reference.

While I might have to wrap this in a App anyway I dont know its working as far as I now have a running Launch Agent with a pid when I set a KeepAlive in the Service Property list and a XPC listener which prints active (its also started with dispatchMain())

but trying to send something from the (client) main App ends up with failed at lookup with error 3 - No such process and in console failed to do a bootstrap look-up: xpc_error=[3: No such process]

I tried naming the Service with my teamIdentifier in front and without, only for the service and everywhere also in the helper bundle ID - but also just without the Team ID and with and without a AppGroup

but it looks like something is still preventing them from searing each other - Might be AppGroups dont work on command line tools because I added the group via the entitlements file, not in the Capabilities Editor because it doesn't show up.

In the thread was already mentioned could be a problem to have entitlements probably next step is wrapping it anyway in a App target

Well - I think I found my problem and wow it's a weird one.

After reading all the sample code related to XPC again and adding my teamID again to the bundle identifier (XXXXXXXXX.com.myapp.helper) I saw Xcode replacing the first character of the teamID with a dash -

It always did that and I put that to "Xcode weirdness" Because bundle Identifiers are reverse dns format this should obviously not start with a digit - well unlucky situation here

Well now this is a problem. My TeamIdentifier starts with a 4.

My bundleID 4T9XXXXXX.com.myapp.helper gets immediately after submitting the textfield replaced with -T9XXXXXX.com.myapp.helper. When I build the App and check its contents Info.plist - the bundleidentifer is the wrong one with a dash. I think this took me all over the place because I ignored it for year when I saw it. After setting the Product Name and bundle Identifiers manually, it took some tries of changing the plist but after some tries it did stick.

after reviewing again AppSandboxLoginitemXPCDemo readme I now think I should have everything

  • Team Identifier in BundleID XXXXXXX.com.myapp.Helper
  • lauchagent plist with Label: XXXXXXX.com.myapp.Helper
  • BundleProgram: Contents/MacOS/XxXX.com.myapp.Helper
  • gets exported as product name XXXXXXX.com.myapp.Helper
  • gets placed into Content/MacOS/XXXXXX.com.myapp.Helper.app
  • launch plist com.my.app.helper in Library/LaunchAgents/com.myapp.Helper.plist <- I dont think it matters here whether there is the team Identifier here
  • App group
  • Helper and main App Sandboxed and Hardened Runtime (its for the Mac App Store)

It is possible to this in a Appstore Application? I would think this XPC Service is made for this situation but I just cant get it working.

But since switching from command line tool to App target for the helper I can start it fine in Xcode but starting the plist after registering it with SMAppService.agent() doesn't really work ( I think, I have problems debugging this)

my main.swift of the Helper is currently just this

os_log("Hello world")
dispatchMain()

force - starting with launchctl start XXXXXX.com.myapp.Helper doesn't even log this to console.

Questions anyone can help me with:

  • How can I debug my LaunchAgent?

I dont think it's even starting but it might even return too fast? Running the target in Xcode prints Hello World fine.

  • Do I need to buy another Developer account to get away from my starting digit Team ID?

There might be more wrong with signing with my Team Identifier? Is there a good way to check all this? Everything I looked at looks okay for now. Xcode is also really not happy with me starting Product Module Name with a 4, but I need this and Product name also XXXX.com.myapp.Helper (file names have to be this according to AppSandboxLoginitemXPCDemo readme) and only with Product Module Name I dont see any errors like this:

effectiveDisposition: getRelationship failed, url=file:///Users/......app/Contents/Library/LaunchAgents/com.myapp.Helper.plist, error: Error Domain=NSCocoaErrorDomain Code=3328 "The requested operation couldn’t be completed because the feature is not supported." UserInfo={NSUnderlyingError=0x7f845100aed0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}

But Xcode complains now with Red Error its not a valid Identifier for Module Name - but it still builds and runs - But this really makes me feel this is a really bad Team ID to have if you need to use LaunchAgents.

Writing the AppStore Team for a new TeamIdentifier is something I might try but more of long shot I guess. I dont think many people encounter this or will ever. There are 3 posts about this on the Internet and all of them have a starting digit.

  • Is this somehow a signing problem?

I have also seen this:

failed to fetch /Users/myapp.app/Contents/_CodeSignature/CodeRequirements-1 error=-10

I embed the Helper.app into the main App and its using automatic signing. Contents/_CodeSignature/CodeRequirements is there.

Any help is very much appreciated its a wild ride Benjamin

Okay I thought now removing com.apple.security.inherit brings it in its own Sandbox (its needs sandboxing) but this also crashes the process because of the sandbox.

It’s possible to enable App Sandbox on a launchd agent, but it’s a bit tricky. For the App Sandbox to start, it needs to know your process’s bundle ID. By default a launchd agent is a standalone executable that has no bundle ID. You can fix this in one of two ways:

  • Embed your agent in an app-like wrapper, per Signing a daemon with a restricted entitlement.

    IMPORTANT The App Sandbox entitlement isn’t restricted, so this article isn’t an exact match for your situation. However, both problems require the same solution, namely embedding a standalone executable in a ‘dummy’ bundle.

  • Give your agent’s executable an Info.plist via the Create Info.plist Section in Binary build setting.

The former approach only works when you’re installing the agent yourself, because you control the entire launchd property list. Use the latter approach if you’re using SMAppService to install your agent.

This should be sufficient to get your agent up and running. I’m not sure if other stuff, like Safari, will block your XPC connection. However, using an XPC service name that’s ‘nested within’ an app group that’s authorised by the entitlement does work with a sandboxed app and agent. I recently had cause to test that as part of a DTS incident.


Oh blah, I shoulda scrolled down )-: The above is my response to your first post. The following is in response to your subsequent ones.


Hmmm, on reading that you seem to have gone down the sandboxed login item path, which should work (modulo Xcode’s wacky bundle ID behaviour) but it also shouldn’t be necessary. I think you’d be better off installing an standard agent via SMAppService. Those agents follow all the normal rules, whereas sandboxed login items are… well… how to put this kinda… OK, I can’t put it kindly so I’ll just shut up now (-:

So, here are my notes on how to get this working with SMAppService

First, the app installs the agent using SMAppLaunch. That mechanism is compatible with the App Sandbox (I was pleased to discover :-).

Next, the agent must be sandboxed. This presents some challenges. The first relates to code signing entitlements. I followed the path described in Embedding a command-line tool in a sandboxed app with one exception: The agent is run by the system rather than as a child process of the app, and thus it must not be signed with the com.apple.security.inherit entitlement.

IMPORTANT Pay attention to the discussion of the Code Signing Inject Base Entitlements (CODE_SIGN_INJECT_BASE_ENTITLEMENTS) build setting.

The second challenge is that the agent needs a proper Info.plist. That’s because the agent is starting up in a new sandbox, and the system needs to know the bundle ID to associate with that sandbox. The agent is a standalone executable, so it can’t have a normal Info.plist file. Rather, I set the Create Info.plist Section in Binary build setting.

Finally, there’s the question of IPC. One little known feature of app groups is that, by using an app group ID as a Mach service name prefix, you can communicate between sandboxed apps. In this case, the agent and app can both access the TTT.com.example.apple-samplecode.SandboxedAgent.group app group (where TTT is my Team ID), and thus the agent can register and listen on the TTT.com.example.apple-samplecode.SandboxedAgent.group.agent named XPC endpoint and the app can connect to it. Sneaky!

As both programs are sandboxed, they must be signed with the app group entitlement to authorise their access to that app group. However, on macOS that’s an unrestricted entitlement, so the agent does not need a provisioning profile to authorise its use of that entitlement (which is good because there would be no way to set that up :-). For more on this, see App Groups: macOS vs iOS: Fight!.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Oh blah, I shoulda scrolled down )-: The above is my response to your first post. The following is in response to your subsequent ones.

I'm sorry I already tried to shorten it up - and failed :-D Thank you for reading through!

The former approach only works when you’re installing the agent yourself, because you control the entire launchd property list. Use the latter approach if you’re using SMAppService to install your agent.

I was actually using SMAppService.agent(plistName: )the whole time, I just took the information about the Bundle Identifier in AppSandboxLoginItemXPCDemo because I couldn't make the way with just the groups work. That was what I originally tried but never worked and I thought this is the way to do it. Or am I using this wrong? I'm copying the plist file with a build phase to Destination Wrapper into Contents/Library/LaunchAgents. This is the SMAppService way or do I have to do something different? Or is this mixed way between AppService and LoginItem?

instead of using a command line target I wrapped it in a App because I wasn't sure if AppGroups work on it. Should I just bundle that in a dummy bundle? Or is App the way to go?

Finally, there’s the question of IPC. One little known feature of app groups is that, by using an app group ID as a Mach service name prefix, you can communicate between sandboxed apps. In this case, the agent and app can both access the TTT.com.example.apple-samplecode.SandboxedAgent.group app group (where TTT is my Team ID), and thus the agent can register and listen on the TTT.com.example.apple-samplecode.SandboxedAgent.group.agent named XPC endpoint and the app can connect to it. Sneaky!

Hm, I wanted to do it this way but I think I did try different group/name syntax - and I now redid it again and its stilll failing - and it feels like at the same step.

I basically mixed what I learned from Signing a daemon with a restricted entitlement and Embedding a command-line tool in a sandboxed app

I will summarise what I did, maybe you can sport something because this really feels like something simple. I'm not sure if I'm doing the right thing with the group names and the XCP endpoint - but before that - my process is never there.

Main.app

  • BundleIdentifier: com.myapp
  • AppGroup: ***.com.myapp.group

Helper.app

  • Its an .app Target
  • BundleIdentifier: com.myapp.helper
  • Entitlements: just Sandbox and Hardened Runtime
  • AppGroup: ***.com.myapp.group
  • disabled CODE_SIGN_INJECT_BASE_ENTITLEMENTS
  • enabled CREATE_INFOPLIST_SECTION_IN_BINARY
  • Gets embedded to Destination: Executables

I checked for my application-identifier in the entitlements like described in Signing a daemon with a restricted entitlement and it was missing it. I added Custom Network Protocol Capability to Entitlements just to get an automatic provisioning profile that's embedded in the Helper.app and test if that is the problem. But it doesn't matter, with or without it doesn't change.

com.myapp.Helper.plist

  • Label: com.myapp.Helper

  • BundleProgram: Contents/MacOS/Helper.app

  • MachServices: XXXX.myapp.Helper true <-- do I actually need this?

  • Gets copied to to Destination: Wrappers - Contents/Library/LaunchAgents

listening XPC Connection

***.com.myapp.group.Helper

This is different. I tried listening on just the group or the helper bundle Identifier but not a mix of .group.Name. I dont think you did a typo there. But I cant try it it really because of the problems below

But my XPC Connection always fails the same way failed at lookup with error 3 - No such process

And I believe it - there never is such process, but why? I think my problem lies on getting the launch agent started correctly or there is no such process because its a sandboxing situation?

After registering the LaunchAgent in my App with SMAppService.agent(plistName: ) this is happening in console:

effectiveDisposition: getRelationship failed, url=file:///Users/user/Library/Developer/Xcode/DerivedData/myapp-hdapfcmolqdbcfeymucseidhmqzh/Build/Products/Debug/myapp/Contents/Library/LaunchAgents/com.myapp.Helper.plist, error: Error Domain=NSCocoaErrorDomain Code=3328 "The requested operation couldn’t be completed because the feature is not supported." UserInfo={NSUnderlyingError=0x7f9535a0f5f0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}

No such file - It looks like it cant launch because the executable is not there. But Helper.app is in Contents/MacOS and the plist is in LaucnhAgents

effectiveDisposition: getRelationship failed, item=uuid=5E6A0905-4B0C-4EA7-B09A-47F0DC6F69D6, name=Helper.app, type=agent, disposition=[enabled, allowed, visible, notified], identifier=com.myapp.Helper, url=Contents/Library/LaunchAgents/com.myapp.Helper.plist -- file:///

this one looks almost cut off - there is nothing after file:///

copyJobWithLabel for label com.myapp.Helper failed with error 113: Could not find specified service
itemWithAppURL: appURL=file:///Users/user/Library/Developer/Xcode/DerivedData/myapp-hdapfcmolqdbcfeymucseidhmqzh/Build/Products/Debug/myapp.app/, type=agent, url=Contents/Library/LaunchAgents/com.myapp.Helper.plist -- file:///, config={
    BTMConfigArguments =     (
    );
    BTMConfigBundleIdentifiers =     (
    );
    BTMConfigExecutablePath = "Contents/MacOS/Helper.app";
    BTMConfigLabel = "com.myapp.Helper";
    BTMConfigSHA256Checksum = {length = 32, bytes = 0xcf54e082 1fda0f68 9988aa21 227bd4d7 ... 51f3d096 7aefaabc };
} uid=501

I have moved around everything I could think of but it's just not happy. I'm guessing I'm doing some obvious thing wrong but it all looks like its at the right place

The problèm seems to start at my LaunchAgent isn't starting but I have no idea why.

Thank you!

Benjamin

I checked for my application-identifier in the entitlements

You don’t need this entitlement and, if it were present, it’d probably cause problems.

The App ID entitlement is used to match an app with its provisioning profile, as discussed in TN3125 Inside Code Signing: Provisioning Profiles. Your launchd agent can’t have a profile, because it’s not embedded in a bundle structure, and thus you can’t the App ID entitlement, or any other restricted entitlement that must be authorised by a profile. Fortunately, the entitlements you do need to use, App Sandbox and app group, or not unrestricted [1].

Your attempt to get this to work has lead you down the wrong path, namely trying to put your launchd agent’s executable in a bundle wrapper. That’s not necessary and definitely complicates things. I recommend that you use a standalone executable.

Here’s what this looked like in my test project:

% find SandboxedAgentApp.app
SandboxedAgentApp.app
SandboxedAgentApp.app/Contents
SandboxedAgentApp.app/Contents/_CodeSignature
SandboxedAgentApp.app/Contents/_CodeSignature/CodeResources
SandboxedAgentApp.app/Contents/MacOS
SandboxedAgentApp.app/Contents/MacOS/SandboxedAgentApp
SandboxedAgentApp.app/Contents/MacOS/SandboxedAgent
SandboxedAgentApp.app/Contents/Resources
SandboxedAgentApp.app/Contents/Resources/MainMenu.nib
SandboxedAgentApp.app/Contents/Library
SandboxedAgentApp.app/Contents/Library/LaunchAgents
SandboxedAgentApp.app/Contents/Library/LaunchAgents/com.example.apple-samplecode.SandboxedAgent.agent.plist
SandboxedAgentApp.app/Contents/Info.plist
SandboxedAgentApp.app/Contents/PkgInfo
% plutil -p SandboxedAgentApp.app/Contents/Library/LaunchAgents/com.example.apple-samplecode.SandboxedAgent.agent.plist
{
  "BundleProgram" => "Contents/MacOS/SandboxedAgent"
  "Label" => "com.example.apple-samplecode.SandboxedAgent.agent"
  "MachServices" => {
    "SKMME9E2Y8.com.example.apple-samplecode.SandboxedAgent.group.agent" => 1
  }
}
% codesign -d --entitlements - SandboxedAgentApp.app
…
[Dict]
	[Key] com.apple.security.app-sandbox
	[Value]
		[Bool] true
	[Key] com.apple.security.application-groups
	[Value]
		[Array]
			[String] SKMME9E2Y8.com.example.apple-samplecode.SandboxedAgent.group
	[Key] com.apple.security.get-task-allow
	[Value]
		[Bool] true
% codesign -d --entitlements - SandboxedAgentApp.app/Contents/MacOS/SandboxedAgent
…
[Dict]
	[Key] com.apple.security.app-sandbox
	[Value]
		[Bool] true
	[Key] com.apple.security.application-groups
	[Value]
		[Array]
			[String] SKMME9E2Y8.com.example.apple-samplecode.SandboxedAgent.group

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] On macOS. On other platforms all entitlements are restricted.

Your attempt to get this to work has lead you down the wrong path, namely trying to put your launchd agent’s executable in a bundle wrapper. That’s not necessary and definitely complicates things. I recommend that you use a standalone executable.

Thank you, I kinda expected that. I've gone back to the executable version, checked again, slept on it and did another couple new test projects but I cant get it to work.

I sent in a DTS case (53735199), just before the holiday shutdown - I'm sorry! :-)

I made a test project SandboxTest.app and attached it to the case, it is my version of SandboxedAgentApp.app to test this.

I sent in a DTS case (53735199), just before the holiday shutdown - I'm sorry! :-)

You’re assuming I’m still at work (-:

Today is my last day of work for 2023, and this didn’t make it to my queue last night which means I probably won’t get to it today. I’ll leave a breadcrumb so that other folks on my team can get a head start on it.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"