/* * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The 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. * * @APPLE_LICENSE_HEADER_END@ */ // // trampolineClient - Authorization trampoline client-side implementation // #include #include #include #include #include #include #include #include // // Where is the trampoline itself? // #if !defined(TRAMPOLINE) # define TRAMPOLINE "/usr/libexec/security_authtrampoline" /* 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; }