]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_authorization/lib/trampolineClient.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_authorization / lib / trampolineClient.cpp
diff --git a/libsecurity_authorization/lib/trampolineClient.cpp b/libsecurity_authorization/lib/trampolineClient.cpp
new file mode 100644 (file)
index 0000000..e25befd
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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_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);
+
+
+//
+// The public client API function.
+//
+OSStatus AuthorizationExecuteWithPrivileges(AuthorizationRef authorization,
+       const char *pathToTool,
+       AuthorizationFlags flags,
+       char *const *arguments,
+       FILE **communicationsPipe)
+{
+       // 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;
+
+       // 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");
+                       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]); }
+                               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;
+                               error = h2n(error);
+                               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;
+}