Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mac sandbox: running a binary tool that needs /tmp

I have a sandboxed Cocoa app that, during an export process, needs to run a third party command-line tool. This tool appears to be hardcoded to use /tmp for its temporary files; sandboxing doesn't permit access to this folder, so the export fails.

How can I get this tool to run? I don't have access to its source code, so I can't modify it to use NSTemporaryDirectory(), and it doesn't appear to respect the TMP or TEMPDIR environment variables. For reasons I don't understand, giving myself a com.apple.security.temporary-exception.files.absolute-path.read-write entitlement doesn't seem to work, either.

Is there some way to re-map folders within my sandbox? Is there some obscure trick I can use? Should I try to patch the tool's binary somehow? I'm at my wit's end here.

like image 587
Becca Royal-Gordon Avatar asked Aug 25 '14 01:08

Becca Royal-Gordon


3 Answers

I was able to get user3159253's DYLD_INSERT_LIBRARIES approach to work. I'm hoping they will write an answer describing how that works, so I'll leave the details of that out and explain the parts that ended up being specific to this case.

Thanks to LLDB, elbow grease, and not a little help from Hopper, I was able to determine that the third-party tool used mkstemp() to generate its temporary file names, and some calls (not all) used a fixed template starting with /tmp. I then wrote a libtmphack.dylib that intercepted calls to mkstemp() and modified the parameters before calling the standard library version.

Since mkstemp() takes a pointer to a preallocated buffer, I didn't feel like I could rewrite a path starting with a short string like "/tmp" to the very long string needed to get to the Caches folder inside the sandbox. Instead, I opted to create a symlink to it called "$tmp" in the current working directory. This could break if the tool chdir()'d at an inopportune time, but fortunately it doesn't seem to do that.

Here's my code:

//
//  libtmphack.c
//  Typesetter
//
//  Created by Brent Royal-Gordon on 8/27/14.
//  Copyright (c) 2014 Groundbreaking Software. This file is MIT licensed.
//

#include "libtmphack.h"
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
//#include <errno.h>
#include <string.h>

static int gbs_has_prefix(char * needle, char * haystack) {
    return strncmp(needle, haystack, strlen(needle)) == 0;
}

int mkstemp(char *template) {
    static int (*original_mkstemp)(char * template) = NULL;

    if(!original_mkstemp) {
        original_mkstemp = dlsym(RTLD_NEXT, "mkstemp");
    }

    if(gbs_has_prefix("/tmp", template)) {
        printf("libtmphack: rewrote mkstemp(\"%s\") ", template);
        template[0] = '$';
        printf("to mkstemp(\"%s\")\n", template);

        // If this isn't successful, we'll presume it's because it's already been made
        symlink(getenv("TEMP"), "$tmp");

        int ret = original_mkstemp(template);

        // Can't do this, the caller needs to be able to open the file
        // int retErrno = errno;
        // unlink("$tmp");
        // errno = retErrno;

        return ret;
    }
    else {
        printf("libtmphack: OK with mkstemp(\"%s\")\n", template);
        return original_mkstemp(template);
    }
}

Very quick and dirty, but it works like a charm.

like image 139
Becca Royal-Gordon Avatar answered Oct 21 '22 23:10

Becca Royal-Gordon


Since @BrentRoyal-Gordon has already published a working solution I'm simply duplicating my comment which inspired him to produce the solution:

In order to fix a program behavior, I would intercept and override some system calls with the help of DYLD_INSERT_LIBRARIES and a custom shared library with a custom implementation of the given system calls.

The exact list of the syscalls which need to be overridden depends on nature of the application and can be studied with a number of tools built upon MacOS DTrace kernel facility. E.g. dtruss or Hopper. @BrentRoyal-Gordon has investigated that the app can be fixed solely with an /appropriate/ implementation of mkstemp.

That's it. I'm still not sure that I've deserved the bounty :)

like image 37
user3159253 Avatar answered Oct 22 '22 01:10

user3159253


Another solution would be to use chroot within the child process (or posix_spawn options) to change its root directory to a directory that is within your sandbox. Its “/tmp” will then be a “tmp” directory within that directory.

like image 23
Peter Hosey Avatar answered Oct 22 '22 01:10

Peter Hosey