X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_authorization/lib/trampolineClient.cpp diff --git a/Security/libsecurity_authorization/lib/trampolineClient.cpp b/Security/libsecurity_authorization/lib/trampolineClient.cpp new file mode 100644 index 00000000..a640f0a0 --- /dev/null +++ b/Security/libsecurity_authorization/lib/trampolineClient.cpp @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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 +#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); + + +OSStatus AuthorizationExecuteWithPrivileges(AuthorizationRef authorization, + const char *pathToTool, + AuthorizationFlags flags, + char *const *arguments, + FILE **communicationsPipe) +{ + // externalize the authorization + AuthorizationExternalForm extForm; + if (OSStatus err = AuthorizationMakeExternalForm(authorization, &extForm)) + return err; + + return AuthorizationExecuteWithPrivilegesExternalForm(&extForm, pathToTool, flags, arguments, communicationsPipe); +} + +// +// The public client API function. +// +OSStatus AuthorizationExecuteWithPrivilegesExternalForm(const AuthorizationExternalForm * extForm, + const char *pathToTool, + AuthorizationFlags flags, + char *const *arguments, + FILE **communicationsPipe) +{ + if (extForm == NULL) + return errAuthorizationInvalidPointer; + + // report the caller to the authorities + aslmsg m = asl_new(ASL_TYPE_MSG); + asl_set(m, "com.apple.message.domain", "com.apple.libsecurity_authorization.AuthorizationExecuteWithPrivileges"); + asl_set(m, "com.apple.message.signature", getprogname()); + asl_log(NULL, m, ASL_LEVEL_NOTICE, "AuthorizationExecuteWithPrivileges!"); + asl_free(m); + + // flags are currently reserved + if (flags != 0) + return errAuthorizationInvalidFlags; + + // 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); + + // compute the argument vector here because we can't allocate memory once we fork. + + // make text representation of the temp-file descriptor + char mboxFdText[20]; + snprintf(mboxFdText, sizeof(mboxFdText), "auth %d", fileno(mbox)); + + // where is the trampoline? +#if defined(NDEBUG) + const char *trampoline = TRAMPOLINE; +#else //!NDEBUG + const char *trampoline = getenv("AUTHORIZATIONTRAMPOLINE"); + if (!trampoline) + trampoline = TRAMPOLINE; +#endif //NDEBUG + + const char **argv = argVector(trampoline, pathToTool, mboxFdText, arguments); + + // 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; + } + + OSStatus status = errSecSuccess; + + // 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 + secdebug("authexec", "parent waiting for status"); + ssize_t rc = read(notify[READ], &status, sizeof(status)); + status = n2h(status); + switch (rc) { + 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=%d", (int)status); + close(notify[READ]); + if (communicationsPipe) { close(comm[READ]); close(comm[WRITE]); } + goto exit_point; + case 0: // end of file: exec succeeded + close(notify[READ]); + if (communicationsPipe) + *communicationsPipe = fdopen(comm[READ], "r+"); + secdebug("authexec", "parent resumes (no error)"); + status = errSecSuccess; + goto exit_point; + } + } + break; + + 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); + } + + // okay, execute the trampoline + if (argv) + execv(trampoline, (char *const*)argv); + + // execute failed - tell the parent + { + OSStatus error = errAuthorizationToolExecuteFailure; + error = h2n(error); + write(1, &error, sizeof(error)); + _exit(1); + } + } + } + +exit_point: + free(argv); + return status; +} + + +// +// 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; +}