trampolineClient.cpp [plain text]
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <Security/Authorization.h>
#include <Security/debugging.h>
#if !defined(TRAMPOLINE)
# define TRAMPOLINE "/System/Library/CoreServices/AuthorizationTrampoline"
#endif
enum {
READ = 0, WRITE = 1 };
static const char **argVector(const char *trampoline,
const char *tool, const char *commFd,
char *const *arguments);
OSStatus AuthorizationExecuteWithPrivileges(AuthorizationRef authorization,
const char *pathToTool,
unsigned long flags,
char *const *arguments,
FILE **communicationsPipe)
{
if (flags != 0)
return errAuthorizationInvalidFlags;
AuthorizationExternalForm extForm;
if (OSStatus err = AuthorizationMakeExternalForm(authorization, &extForm))
return err;
FILE *mbox = tmpfile();
if (!mbox)
return errAuthorizationInternal;
if (fwrite(&extForm, sizeof(extForm), 1, mbox) != 1) {
fclose(mbox);
return errAuthorizationInternal;
}
fflush(mbox);
char mboxFdText[20];
snprintf(mboxFdText, sizeof(mboxFdText), "auth %d", fileno(mbox));
int notify[2];
if (pipe(notify)) {
fclose(mbox);
return errAuthorizationToolExecuteFailure;
}
int comm[2];
if (communicationsPipe && socketpair(AF_UNIX, SOCK_STREAM, 0, comm)) {
close(notify[READ]); close(notify[WRITE]);
fclose(mbox);
return errAuthorizationToolExecuteFailure;
}
int delay = 1;
for (int n = 5;; n--, delay *= 2) {
switch (pid_t pid = fork()) {
case -1: if (errno == EAGAIN) {
if (n > 0) {
debug("authexec", "resource shortage (EAGAIN), delaying %d seconds", delay);
sleep(delay);
continue;
}
}
debug("authexec", "fork failed (errno=%d)", errno);
close(notify[READ]); close(notify[WRITE]);
return errAuthorizationToolExecuteFailure;
default: close(notify[WRITE]);
if (communicationsPipe)
close(comm[WRITE]);
fclose(mbox);
OSStatus status;
debug("authexec", "parent waiting for status");
switch (ssize_t rc = read(notify[READ], &status, sizeof(status))) {
default: debug("authexec", "unexpected read return value %ld", long(rc));
status = errAuthorizationToolEnvironmentError;
case sizeof(status): debug("authexec", "parent received status=%ld", status);
close(notify[READ]);
if (communicationsPipe) { close(comm[READ]); close(comm[WRITE]); }
return status;
case 0: close(notify[READ]);
if (communicationsPipe)
*communicationsPipe = fdopen(comm[READ], "r+");
debug("authexec", "parent resumes (no error)");
return noErr;
}
case 0: close(notify[READ]);
if (communicationsPipe)
close(comm[READ]);
dup2(notify[WRITE], 1);
close(notify[WRITE]);
if (communicationsPipe) {
dup2(comm[WRITE], 0);
close(comm[WRITE]);
} else {
close(0);
open("/dev/null", O_RDWR);
}
#if defined(NDEBUG)
const char *trampoline = TRAMPOLINE;
#else //!NDEBUG
const char *trampoline = getenv("AUTHORIZATIONTRAMPOLINE");
if (!trampoline)
trampoline = TRAMPOLINE;
#endif //NDEBUG
debug("authexec", "child exec(%s:%s)",
trampoline, pathToTool);
if (const char **argv = argVector(trampoline, pathToTool, mboxFdText, arguments))
execv(trampoline, (char *const*)argv);
debug("authexec", "trampoline exec failed (errno=%d)", errno);
{
OSStatus error = errAuthorizationToolExecuteFailure;
write(1, &error, sizeof(error));
_exit(1);
}
}
}
}
static const char **argVector(const char *trampoline, const char *pathToTool,
const char *mboxFdText, char *const *arguments)
{
int length = 0;
if (arguments) {
for (char *const *p = arguments; *p; p++)
length++;
}
if (const char **args = (const char **)malloc(sizeof(const char *) * (length + 4))) {
args[0] = trampoline;
args[1] = pathToTool;
args[2] = mboxFdText;
if (arguments)
for (int n = 0; arguments[n]; n++)
args[n + 3] = arguments[n];
args[length + 3] = NULL;
return args;
}
return NULL;
}