Give sandboxed app access to /var directory

I have an app that runs on macOS Monterey.

For various reasons, I have to externally add a sandbox entitlement (externally, as in using codesign, rather than rebuilding it)

After adding the sandbox entitlement, and resigning appropriately, the app crashes on launch with the following error :

ERROR:process_singleton_posix.cc(1186)] Failed to bind() /var/folders/s2/j0z79krx321qg318das1r95_zc0000gn/T/com.funkyapp/S/SingletonSocket

So I assumed I needed to give access to this file. So I added the following entitlements to the app, via codesign :

<key>com.apple.security.temporary-exception.files.absolute-path.read-write</key> <array> <string>/var</string> <string>/var/folders/s2/j0z79krx321qg318das1r95_zc0000gn/T/com.funkyapp/S/SingletonSocket</string> </array>

and also

<key>com.apple.security.network.client</key> <true/> <key>com.apple.security.network.server</key> <true/>

Unfortunately, it still crashes on load, with the same error.

Does anyone know why that is? From my perspective, I gave the appropriate entitlements to bind a socket at that path, what am I missing?

Thanks !

Accepted Reply

So I assumed I needed to give access to this file.

Sadly, this approach is a non-starter. The message you’re seeing, Failed to bind(), and the last item of the path, SingletonSocket, suggests that the app is using a Unix domain socket. The file system temporary exception entitlements, like com.apple.security.temporary-exception.files.absolute-path.read-write, only work for files (hey, it’s in the name!). So the app will be able to work with files at that path but it won’t be able to work with Unix domain sockets at that path.

A sandboxed app can create Unix domains sockets in its container or in any container that it has access to via an app group. That works just fine, and you can even use it to share the socket between multiple apps from the same team. However, setting that up would require you to change the app’s code.

For various reasons, I have to externally add a sandbox entitlement (externally, as in using codesign, rather than rebuilding it)

That’s going to be really challenging. While you can add the App Sandbox entitlements without changing the code, the code has to actually work in the sandbox. My experience is that any reasonably large app will always have to make code-level changes to support the sandbox. In some cases those changes are minor, but they’re always there.

Share and Enjoy

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

Replies

So I assumed I needed to give access to this file.

Sadly, this approach is a non-starter. The message you’re seeing, Failed to bind(), and the last item of the path, SingletonSocket, suggests that the app is using a Unix domain socket. The file system temporary exception entitlements, like com.apple.security.temporary-exception.files.absolute-path.read-write, only work for files (hey, it’s in the name!). So the app will be able to work with files at that path but it won’t be able to work with Unix domain sockets at that path.

A sandboxed app can create Unix domains sockets in its container or in any container that it has access to via an app group. That works just fine, and you can even use it to share the socket between multiple apps from the same team. However, setting that up would require you to change the app’s code.

For various reasons, I have to externally add a sandbox entitlement (externally, as in using codesign, rather than rebuilding it)

That’s going to be really challenging. While you can add the App Sandbox entitlements without changing the code, the code has to actually work in the sandbox. My experience is that any reasonably large app will always have to make code-level changes to support the sandbox. In some cases those changes are minor, but they’re always there.

Share and Enjoy

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

Ah right, that all makes sense.

Is there any way to add the directory /var/folders/s2/j0z79krx321qg318das1r95_zc0000gn/T/com.funkyapp to the app's container, or to the app group's container?

The weird thing about this is that this path looks like a temporary directory and, in general, app’s have unrestricted access to their temporary directory.

Some tests reveal something that I wasn’t previously aware of. When I wrote:

A sandboxed app can create Unix domains sockets in its container

I was more accurate than I thought. Normally I’d consider the temporary directory to be part of the app’s main container, but that’s actually not the case. Consider the test code below. Calling it like this:

NSURL * tempDir = NSFileManager.defaultManager.temporaryDirectory;
[self testWithinDirectoryURL:tempDir];

fails with EPERM but calling it like this:

NSURL * docsURL = [NSFileManager.defaultManager URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
[self testWithinDirectoryURL:docsURL];

works. So, yeah, it really is limited to the container.

Is there any way to add the directory … to the app's container, or to the app group's container?

No. The fix here is to change the app to not use the temporary directory, which is a problem given your constraints.

Share and Enjoy

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


- (void)testWithinDirectoryURL:(NSURL *)directoryURL {
    NSURL * sockURL = [directoryURL URLByAppendingPathComponent:@"test.sock"];

    (void) [NSFileManager.defaultManager removeItemAtURL:sockURL error:NULL];

    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    assert(fd >= 0);
    
    struct sockaddr_un addr = {};
    addr.sun_family = AF_UNIX;
    strlcpy(addr.sun_path, sockURL.fileSystemRepresentation, sizeof(addr.sun_path));
    size_t len = SUN_LEN(&addr);
    assert(len < 256);
    addr.sun_len = (uint8_t) len;
    
    BOOL success = bind(fd, (const struct sockaddr *) &addr, sizeof(addr)) >= 0;
    NSLog(@"--");
    NSLog(@"%@", sockURL);
    if (success) {
        NSLog(@"bind succeeded");
    } else {
        NSLog(@"bind failed, error; %d", errno);
    }
    (void) close(fd);
}

Ah, thanks that's helpful.

I understood from your previous reply that I needed to change the code. However, I am still struggling with this, even after going into the codebase. It is an electron app. I thought that moving the electron build from platform=darwin to platform=mas would be enough.

Unfortunately I still hit the exact same snag. This is surprising to me, as I think the socket is created by the electron platform (for communication between the different processes).

Do you have any knowledge of electron by any chance, and do you have any random ideas that you could point me to?

Thanks!

Do you have any knowledge of electron by any chance … ?

No, sorry. My advice is that you escalate this via its support channel, where you’re more likely to find folks with relevant experience.

Share and Enjoy

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

  • That makes sense, I will do that. Thanks.

Add a Comment