1/* 2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// trampolineClient - Authorization trampoline client-side implementation 27// 28#include <asl.h> 29#include <sys/types.h> 30#include <unistd.h> 31#include <errno.h> 32#include <fcntl.h> 33#include <stdlib.h> 34#include <sys/socket.h> 35#include <Security/Authorization.h> 36#include <Security/AuthorizationPriv.h> 37#include <Security/SecBase.h> 38#include <security_utilities/endian.h> 39#include <security_utilities/debugging.h> 40 41// 42// Where is the trampoline itself? 43// 44#if !defined(TRAMPOLINE) 45# define TRAMPOLINE "/usr/libexec/security_authtrampoline" /* fallback */ 46#endif 47 48 49// 50// A few names for clarity's sake 51// 52enum { 53 READ = 0, // read end of standard UNIX pipe 54 WRITE = 1 // write end of standard UNIX pipe 55}; 56 57 58// 59// Local (static) functions 60// 61static const char **argVector(const char *trampoline, 62 const char *tool, const char *commFd, 63 char *const *arguments); 64 65 66OSStatus AuthorizationExecuteWithPrivileges(AuthorizationRef authorization, 67 const char *pathToTool, 68 AuthorizationFlags flags, 69 char *const *arguments, 70 FILE **communicationsPipe) 71{ 72 // externalize the authorization 73 AuthorizationExternalForm extForm; 74 if (OSStatus err = AuthorizationMakeExternalForm(authorization, &extForm)) 75 return err; 76 77 return AuthorizationExecuteWithPrivilegesExternalForm(&extForm, pathToTool, flags, arguments, communicationsPipe); 78} 79 80// 81// The public client API function. 82// 83OSStatus AuthorizationExecuteWithPrivilegesExternalForm(const AuthorizationExternalForm * extForm, 84 const char *pathToTool, 85 AuthorizationFlags flags, 86 char *const *arguments, 87 FILE **communicationsPipe) 88{ 89 if (extForm == NULL) 90 return errAuthorizationInvalidPointer; 91 92 // report the caller to the authorities 93 aslmsg m = asl_new(ASL_TYPE_MSG); 94 asl_set(m, "com.apple.message.domain", "com.apple.libsecurity_authorization.AuthorizationExecuteWithPrivileges"); 95 asl_set(m, "com.apple.message.signature", getprogname()); 96 asl_log(NULL, m, ASL_LEVEL_NOTICE, "AuthorizationExecuteWithPrivileges!"); 97 asl_free(m); 98 99 // flags are currently reserved 100 if (flags != 0) 101 return errAuthorizationInvalidFlags; 102 103 // create the mailbox file 104 FILE *mbox = tmpfile(); 105 if (!mbox) 106 return errAuthorizationInternal; 107 if (fwrite(extForm, sizeof(*extForm), 1, mbox) != 1) { 108 fclose(mbox); 109 return errAuthorizationInternal; 110 } 111 fflush(mbox); 112 113 // compute the argument vector here because we can't allocate memory once we fork. 114 115 // make text representation of the temp-file descriptor 116 char mboxFdText[20]; 117 snprintf(mboxFdText, sizeof(mboxFdText), "auth %d", fileno(mbox)); 118 119 // where is the trampoline? 120#if defined(NDEBUG) 121 const char *trampoline = TRAMPOLINE; 122#else //!NDEBUG 123 const char *trampoline = getenv("AUTHORIZATIONTRAMPOLINE"); 124 if (!trampoline) 125 trampoline = TRAMPOLINE; 126#endif //NDEBUG 127 128 const char **argv = argVector(trampoline, pathToTool, mboxFdText, arguments); 129 130 // make a notifier pipe 131 int notify[2]; 132 if (pipe(notify)) { 133 fclose(mbox); 134 return errAuthorizationToolExecuteFailure; 135 } 136 137 // make the communications pipe if requested 138 int comm[2]; 139 if (communicationsPipe && socketpair(AF_UNIX, SOCK_STREAM, 0, comm)) { 140 close(notify[READ]); close(notify[WRITE]); 141 fclose(mbox); 142 return errAuthorizationToolExecuteFailure; 143 } 144 145 OSStatus status = errSecSuccess; 146 147 // do the standard forking tango... 148 int delay = 1; 149 for (int n = 5;; n--, delay *= 2) { 150 switch (fork()) { 151 case -1: // error 152 if (errno == EAGAIN) { 153 // potentially recoverable resource shortage 154 if (n > 0) { 155 secdebug("authexec", "resource shortage (EAGAIN), delaying %d seconds", delay); 156 sleep(delay); 157 continue; 158 } 159 } 160 secdebug("authexec", "fork failed (errno=%d)", errno); 161 close(notify[READ]); close(notify[WRITE]); 162 return errAuthorizationToolExecuteFailure; 163 164 default: { // parent 165 // close foreign side of pipes 166 close(notify[WRITE]); 167 if (communicationsPipe) 168 close(comm[WRITE]); 169 170 // close mailbox file (child has it open now) 171 fclose(mbox); 172 173 // get status notification from child 174 secdebug("authexec", "parent waiting for status"); 175 ssize_t rc = read(notify[READ], &status, sizeof(status)); 176 status = n2h(status); 177 switch (rc) { 178 default: // weird result of read: post error 179 secdebug("authexec", "unexpected read return value %ld", long(rc)); 180 status = errAuthorizationToolEnvironmentError; 181 // fall through 182 case sizeof(status): // read succeeded: child reported an error 183 secdebug("authexec", "parent received status=%d", (int)status); 184 close(notify[READ]); 185 if (communicationsPipe) { close(comm[READ]); close(comm[WRITE]); } 186 goto exit_point; 187 case 0: // end of file: exec succeeded 188 close(notify[READ]); 189 if (communicationsPipe) 190 *communicationsPipe = fdopen(comm[READ], "r+"); 191 secdebug("authexec", "parent resumes (no error)"); 192 status = errSecSuccess; 193 goto exit_point; 194 } 195 } 196 break; 197 198 case 0: // child 199 // close foreign side of pipes 200 close(notify[READ]); 201 if (communicationsPipe) 202 close(comm[READ]); 203 204 // fd 1 (stdout) holds the notify write end 205 dup2(notify[WRITE], 1); 206 close(notify[WRITE]); 207 208 // fd 0 (stdin) holds either the comm-link write-end or /dev/null 209 if (communicationsPipe) { 210 dup2(comm[WRITE], 0); 211 close(comm[WRITE]); 212 } else { 213 close(0); 214 open("/dev/null", O_RDWR); 215 } 216 217 // okay, execute the trampoline 218 if (argv) 219 execv(trampoline, (char *const*)argv); 220 221 // execute failed - tell the parent 222 { 223 OSStatus error = errAuthorizationToolExecuteFailure; 224 error = h2n(error); 225 write(1, &error, sizeof(error)); 226 _exit(1); 227 } 228 } 229 } 230 231exit_point: 232 free(argv); 233 return status; 234} 235 236 237// 238// Build an argv vector 239// 240static const char **argVector(const char *trampoline, const char *pathToTool, 241 const char *mboxFdText, char *const *arguments) 242{ 243 int length = 0; 244 if (arguments) { 245 for (char *const *p = arguments; *p; p++) 246 length++; 247 } 248 if (const char **args = (const char **)malloc(sizeof(const char *) * (length + 4))) { 249 args[0] = trampoline; 250 args[1] = pathToTool; 251 args[2] = mboxFdText; 252 if (arguments) 253 for (int n = 0; arguments[n]; n++) 254 args[n + 3] = arguments[n]; 255 args[length + 3] = NULL; 256 return args; 257 } 258 return NULL; 259} 260