-/*
- * Copyright (c) 2000-2004 Apple Computer, 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 <asl.h>
-#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/AuthorizationPriv.h>
-#include <Security/SecBase.h>
-#include <security_utilities/endian.h>
-#include <security_utilities/debugging.h>
-
-//
-// 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;
-}