/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ // // trampolineClient - Authorization trampoline client-side implementation // #include #include #include #include #include #include #include #include // // Where is the trampoline itself? // #if !defined(TRAMPOLINE) # define TRAMPOLINE "/System/Library/CoreServices/AuthorizationTrampoline" /* fallback */ #endif // // A few names for clarity's sake // enum { READ = 0, // read end of standard UNIX pipe WRITE = 1 // write end of standard UNIX pipe }; // // Local (static) functions // static const char **argVector(const char *trampoline, const char *tool, const char *commFd, char *const *arguments); // // The public client API function. // OSStatus AuthorizationExecuteWithPrivileges(AuthorizationRef authorization, const char *pathToTool, unsigned long flags, char *const *arguments, FILE **communicationsPipe) { // flags are currently reserved if (flags != 0) return errAuthorizationInvalidFlags; // externalize the authorization AuthorizationExternalForm extForm; if (OSStatus err = AuthorizationMakeExternalForm(authorization, &extForm)) return err; // create the mailbox file FILE *mbox = tmpfile(); if (!mbox) return errAuthorizationInternal; if (fwrite(&extForm, sizeof(extForm), 1, mbox) != 1) { fclose(mbox); return errAuthorizationInternal; } fflush(mbox); // make text representation of the temp-file descriptor char mboxFdText[20]; snprintf(mboxFdText, sizeof(mboxFdText), "auth %d", fileno(mbox)); // make a notifier pipe int notify[2]; if (pipe(notify)) { fclose(mbox); return errAuthorizationToolExecuteFailure; } // make the communications pipe if requested int comm[2]; if (communicationsPipe && socketpair(AF_UNIX, SOCK_STREAM, 0, comm)) { close(notify[READ]); close(notify[WRITE]); fclose(mbox); return errAuthorizationToolExecuteFailure; } // do the standard forking tango... int delay = 1; for (int n = 5;; n--, delay *= 2) { switch (fork()) { case -1: // error if (errno == EAGAIN) { // potentially recoverable resource shortage if (n > 0) { secdebug("authexec", "resource shortage (EAGAIN), delaying %d seconds", delay); sleep(delay); continue; } } secdebug("authexec", "fork failed (errno=%d)", errno); close(notify[READ]); close(notify[WRITE]); return errAuthorizationToolExecuteFailure; default: // parent // close foreign side of pipes close(notify[WRITE]); if (communicationsPipe) close(comm[WRITE]); // close mailbox file (child has it open now) fclose(mbox); // get status notification from child OSStatus status; secdebug("authexec", "parent waiting for status"); switch (IFDEBUG(ssize_t rc =) read(notify[READ], &status, sizeof(status))) { default: // weird result of read: post error secdebug("authexec", "unexpected read return value %ld", long(rc)); status = errAuthorizationToolEnvironmentError; // fall through case sizeof(status): // read succeeded: child reported an error secdebug("authexec", "parent received status=%ld", status); close(notify[READ]); if (communicationsPipe) { close(comm[READ]); close(comm[WRITE]); } return status; case 0: // end of file: exec succeeded close(notify[READ]); if (communicationsPipe) *communicationsPipe = fdopen(comm[READ], "r+"); secdebug("authexec", "parent resumes (no error)"); return noErr; } case 0: // child // close foreign side of pipes close(notify[READ]); if (communicationsPipe) close(comm[READ]); // fd 1 (stdout) holds the notify write end dup2(notify[WRITE], 1); close(notify[WRITE]); // fd 0 (stdin) holds either the comm-link write-end or /dev/null if (communicationsPipe) { dup2(comm[WRITE], 0); close(comm[WRITE]); } else { close(0); open("/dev/null", O_RDWR); } // where is the trampoline? #if defined(NDEBUG) const char *trampoline = TRAMPOLINE; #else //!NDEBUG const char *trampoline = getenv("AUTHORIZATIONTRAMPOLINE"); if (!trampoline) trampoline = TRAMPOLINE; #endif //NDEBUG // okay, execute the trampoline secdebug("authexec", "child exec(%s:%s)", trampoline, pathToTool); if (const char **argv = argVector(trampoline, pathToTool, mboxFdText, arguments)) execv(trampoline, (char *const*)argv); secdebug("authexec", "trampoline exec failed (errno=%d)", errno); // execute failed - tell the parent { OSStatus error = errAuthorizationToolExecuteFailure; write(1, &error, sizeof(error)); _exit(1); } } } } // // Build an argv vector // 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; }