]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_authorization/lib/trampolineClient.cpp
Security-59754.60.13.tar.gz
[apple/security.git] / OSX / libsecurity_authorization / lib / trampolineClient.cpp
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 <sys/types.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <sys/socket.h>
34 #include <Security/SecBase.h>
35 #include <security_utilities/endian.h>
36 #include <security_utilities/debugging.h>
37
38 #include "Security/Authorization.h"
39 #include "AuthorizationPriv.h"
40 #include "AuthorizationTrampolinePriv.h"
41 #include <dispatch/semaphore.h>
42 #include <unistd.h>
43 #include <os/log.h>
44 #include <sys/ioctl.h>
45 #include <sys/poll.h>
46 #include <os/log.h>
47
48 //
49 // A few names for clarity's sake
50 //
51 enum {
52 READ = 0, // read end of standard UNIX pipe
53 WRITE = 1 // write end of standard UNIX pipe
54 };
55
56 static os_log_t AUTH_LOG_DEFAULT() {
57 static dispatch_once_t once;
58 static os_log_t log;
59 dispatch_once(&once, ^{ log = os_log_create("com.apple.Authorization", "Trampoline"); });
60 return log;
61 };
62
63 #define AUTH_LOG AUTH_LOG_DEFAULT()
64
65 //
66 // Where is the trampoline itself?
67 //
68 #if !defined(TRAMPOLINE)
69 # define TRAMPOLINE "/usr/libexec/security_authtrampoline" /* fallback */
70 #endif
71
72
73 //
74 // Local (static) functions
75 //
76 static const char **argVector(const char *trampoline,
77 const char *tool, const char *commFd,
78 char *const *arguments);
79
80
81 OSStatus AuthorizationExecuteWithPrivileges(AuthorizationRef authorization,
82 const char *pathToTool,
83 AuthorizationFlags flags,
84 char *const *arguments,
85 FILE **communicationsPipe)
86 {
87 // externalize the authorization
88 AuthorizationExternalForm extForm;
89 if (OSStatus err = AuthorizationMakeExternalForm(authorization, &extForm))
90 return err;
91
92 return AuthorizationExecuteWithPrivilegesExternalForm(&extForm, pathToTool, flags, arguments, communicationsPipe);
93 }
94
95 //
96 // The public client API function.
97 //
98 OSStatus AuthorizationExecuteWithPrivilegesExternalForm(const AuthorizationExternalForm * extForm,
99 const char *pathToTool,
100 AuthorizationFlags flags,
101 char *const *arguments,
102 FILE **communicationsPipe)
103 {
104 os_log(AUTH_LOG, "AuthorizationExecuteWithPrivileges and AuthorizationExecuteWithPrivilegesExternalForm are deprecated and functionality will be removed soon - please update your application");
105 if (extForm == NULL)
106 return errAuthorizationInvalidPointer;
107
108 // flags are currently reserved
109 if (flags != 0)
110 return errAuthorizationInvalidFlags;
111
112 // compute the argument vector here because we can't allocate memory once we fork.
113
114 // where is the trampoline?
115 #if defined(NDEBUG)
116 const char *trampoline = TRAMPOLINE;
117 #else //!NDEBUG
118 const char *trampoline = getenv("AUTHORIZATIONTRAMPOLINE");
119 if (!trampoline)
120 trampoline = TRAMPOLINE;
121 #endif //NDEBUG
122
123 // make a data exchange pipe
124 int dataPipe[2];
125 if (pipe(dataPipe)) {
126 os_log_error(AUTH_LOG, "data pipe failure");
127 return errAuthorizationToolExecuteFailure;
128 }
129
130 // make text representation of the pipe handle
131 char pipeFdText[20];
132 snprintf(pipeFdText, sizeof(pipeFdText), "auth %d", dataPipe[READ]);
133 const char **argv = argVector(trampoline, pathToTool, pipeFdText, arguments);
134
135 // make a notifier pipe
136 int notify[2];
137 if (pipe(notify)) {
138 close(dataPipe[READ]); close(dataPipe[WRITE]);
139 if(argv) {
140 free(argv);
141 }
142 os_log_error(AUTH_LOG, "notify pipe failure");
143 return errAuthorizationToolExecuteFailure;
144 }
145
146 // make the communications pipe if requested
147 int comm[2];
148 if (communicationsPipe && socketpair(AF_UNIX, SOCK_STREAM, 0, comm)) {
149 close(notify[READ]); close(notify[WRITE]);
150 close(dataPipe[READ]); close(dataPipe[WRITE]);
151 if(argv) {
152 free(argv);
153 }
154 os_log_error(AUTH_LOG, "comm pipe failure");
155 return errAuthorizationToolExecuteFailure;
156 }
157
158 OSStatus status = errSecSuccess;
159
160 // do the standard forking tango...
161 int delay = 1;
162 for (int n = 5;; n--, delay *= 2) {
163 switch (fork()) {
164 case -1: // error
165 if (errno == EAGAIN) {
166 // potentially recoverable resource shortage
167 if (n > 0) {
168 os_log(AUTH_LOG, "resource shortage (EAGAIN), delaying %d seconds", delay);
169 sleep(delay);
170 continue;
171 }
172 }
173 os_log_error(AUTH_LOG, "fork failed (errno=%d)", errno);
174 close(notify[READ]); close(notify[WRITE]);
175 status = errAuthorizationToolExecuteFailure;
176 goto exit_point;
177
178 default: { // parent
179 // close foreign side of pipes
180 close(notify[WRITE]);
181 if (communicationsPipe)
182 close(comm[WRITE]);
183
184 close(dataPipe[READ]);
185 if (write(dataPipe[WRITE], extForm, sizeof(*extForm)) != sizeof(*extForm)) {
186 os_log_error(AUTH_LOG, "fwrite data failed (errno=%d)", errno);
187 status = errAuthorizationInternal;
188 close(notify[READ]);
189 close(dataPipe[WRITE]);
190 if (communicationsPipe) {
191 close(comm[READ]);
192 }
193 goto exit_point;
194 }
195 // get status notification from child
196 os_log_debug(AUTH_LOG, "parent waiting for status");
197 ssize_t rc = read(notify[READ], &status, sizeof(status));
198 status = n2h(status);
199 switch (rc) {
200 default: // weird result of read: post error
201 os_log_error(AUTH_LOG, "unexpected read return value %ld", long(rc));
202 status = errAuthorizationToolEnvironmentError;
203 // fall through
204 case sizeof(status): // read succeeded: child reported an error
205 os_log_error(AUTH_LOG, "parent received status=%d", (int)status);
206 close(notify[READ]);
207 close(dataPipe[WRITE]);
208 if (communicationsPipe) {
209 close(comm[READ]);
210 close(comm[WRITE]);
211 }
212 goto exit_point;
213 case 0: // end of file: exec succeeded
214 close(notify[READ]);
215 close(dataPipe[WRITE]);
216 if (communicationsPipe)
217 *communicationsPipe = fdopen(comm[READ], "r+");
218 os_log_debug(AUTH_LOG, "parent resumes (no error)");
219 status = errSecSuccess;
220 goto exit_point;
221 }
222 }
223
224 case 0: // child
225 // close foreign side of pipes
226 close(notify[READ]);
227 if (communicationsPipe)
228 close(comm[READ]);
229
230 // close write end of the data PIPE
231 close(dataPipe[WRITE]);
232
233 // fd 1 (stdout) holds the notify write end
234 dup2(notify[WRITE], 1);
235 close(notify[WRITE]);
236
237 // fd 0 (stdin) holds either the comm-link write-end or /dev/null
238 if (communicationsPipe) {
239 dup2(comm[WRITE], 0);
240 close(comm[WRITE]);
241 } else {
242 close(0);
243 open("/dev/null", O_RDWR);
244 }
245
246 // okay, execute the trampoline
247 if (argv)
248 execv(trampoline, (char *const*)argv);
249
250 // execute failed - tell the parent
251 {
252 // in case of failure, close read end of the data pipe as well
253 close(dataPipe[WRITE]);
254 close(dataPipe[READ]);
255 OSStatus error = errAuthorizationToolExecuteFailure;
256 error = h2n(error);
257 write(1, &error, sizeof(error));
258 _exit(1);
259 }
260 }
261 }
262
263 exit_point:
264 free(argv);
265 return status;
266 }
267
268
269 //
270 // Build an argv vector
271 //
272 static const char **argVector(const char *trampoline, const char *pathToTool,
273 const char *mboxFdText, char *const *arguments)
274 {
275 int length = 0;
276 if (arguments) {
277 for (char *const *p = arguments; *p; p++)
278 length++;
279 }
280 if (const char **args = (const char **)malloc(sizeof(const char *) * (length + 4))) {
281 args[0] = trampoline;
282 args[1] = pathToTool;
283 args[2] = mboxFdText;
284 if (arguments)
285 for (int n = 0; arguments[n]; n++)
286 args[n + 3] = arguments[n];
287 args[length + 3] = NULL;
288 return args;
289 }
290 return NULL;
291 }
292
293
294
295 OSStatus AuthorizationExecuteWithPrivilegesInternal(const AuthorizationRef authorization,
296 const char * _Nonnull pathToTool,
297 const char * _Nonnull const * arguments,
298 pid_t * newProcessPid,
299 const uid_t uid,
300 int stdOut,
301 int stdErr,
302 int stdIn,
303 void(^processFinished)(const int exitStatus))
304 {
305 // externalize the authorization
306 AuthorizationExternalForm extForm;
307 if (OSStatus err = AuthorizationMakeExternalForm(authorization, &extForm))
308 return err;
309
310 return AuthorizationExecuteWithPrivilegesExternalFormInternal(&extForm, pathToTool, arguments, newProcessPid, uid, stdOut, stdErr, stdIn, processFinished);
311 }
312
313 OSStatus AuthorizationExecuteWithPrivilegesExternalFormInternal(const AuthorizationExternalForm *extAuthorization,
314 const char * _Nonnull pathToTool,
315 const char * _Nullable const * _Nullable arguments,
316 pid_t * newProcessPid,
317 const uid_t uid,
318 int stdOut,
319 int stdErr,
320 int stdIn,
321 void(^processFinished)(const int exitStatus))
322 {
323 xpc_object_t message;
324 __block OSStatus retval = errAuthorizationInternal;
325 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
326 if (!sema) {
327 os_log_error(AUTH_LOG, "Unable to create trampoline semaphore");
328 return retval;
329 }
330 __block xpc_connection_t trampolineConnection = xpc_connection_create_mach_service("com.apple.security.authtrampoline", NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
331
332 if (!trampolineConnection) {
333 os_log_error(AUTH_LOG, "Unable to create trampoline mach service");
334 dispatch_release(sema);
335 return retval;
336 }
337
338 xpc_connection_set_event_handler(trampolineConnection, ^(xpc_object_t event) {
339 xpc_type_t type = xpc_get_type(event);
340
341 if (type == XPC_TYPE_ERROR) {
342 if (trampolineConnection) {
343 xpc_release(trampolineConnection);
344 trampolineConnection = NULL;
345 }
346 if (event == XPC_ERROR_CONNECTION_INTERRUPTED && processFinished) {
347 os_log_error(AUTH_LOG, "Connection with trampoline was interruped");
348 processFinished(134); // simulate killed by SIGABRT
349 }
350 } else {
351 const char *requestId = xpc_dictionary_get_string(event, XPC_REQUEST_ID);
352 if (requestId && strncmp(XPC_EVENT_MSG, requestId, strlen(XPC_EVENT_MSG)) == 0) {
353 const char *eventType = xpc_dictionary_get_string(event, XPC_EVENT_TYPE);
354 if (eventType && strncmp(XPC_EVENT_TYPE_CHILDEND, eventType, strlen(XPC_EVENT_TYPE_CHILDEND)) == 0) {
355 int exitStatus = (int)xpc_dictionary_get_int64(event, RETVAL_STATUS);
356 os_log_debug(AUTH_LOG, "Child process ended with exit status %d", exitStatus);
357
358 if (trampolineConnection) {
359 xpc_connection_cancel(trampolineConnection);
360 xpc_release(trampolineConnection);
361 trampolineConnection = NULL;
362 }
363 if (processFinished) {
364 processFinished(exitStatus);
365 };
366 } else {
367 os_log_error(AUTH_LOG, "Unknown event type [%s] arrived from trampoline", eventType);
368 }
369 } else {
370 os_log_error(AUTH_LOG, "Unknown request [%s] arrived from trampoline", requestId);
371 }
372 }
373 });
374
375 xpc_connection_resume(trampolineConnection);
376
377 message = xpc_dictionary_create(NULL, NULL, 0);
378 xpc_dictionary_set_string(message, XPC_REQUEST_ID, XPC_REQUEST_CREATE_PROCESS);
379
380 Boolean waitForEndNeeded = (processFinished != NULL);
381 if (stdIn >= 0) {
382 xpc_object_t xpcInFd = xpc_fd_create(stdIn);
383 if (!xpcInFd) {
384 os_log_error(AUTH_LOG, "Unable to create XPC stdin FD");
385 goto finish;
386 }
387 xpc_dictionary_set_value(message, PARAM_STDIN, xpcInFd);
388 xpc_release(xpcInFd);
389 waitForEndNeeded = true;
390 }
391
392 if (stdOut >= 0) {
393 xpc_object_t xpcOutFd = xpc_fd_create(stdOut);
394 if (!xpcOutFd) {
395 os_log_error(AUTH_LOG, "Unable to create XPC stdout FD");
396 goto finish;
397 }
398 xpc_dictionary_set_value(message, PARAM_STDOUT, xpcOutFd);
399 xpc_release(xpcOutFd);
400 waitForEndNeeded = true;
401 }
402
403 if (stdErr >= 0) {
404 xpc_object_t xpcErrFd = xpc_fd_create(stdErr);
405 if (!xpcErrFd) {
406 os_log_error(AUTH_LOG, "Unable to create XPC stderr FD");
407 goto finish;
408 }
409 xpc_dictionary_set_value(message, PARAM_STDERR, xpcErrFd);
410 xpc_release(xpcErrFd);
411 waitForEndNeeded = true;
412 }
413
414 extern char** environ;
415
416 if (environ) {
417 xpc_object_t envArray = xpc_array_create(NULL, 0);
418 char **ptr = environ;
419
420 while (*ptr) {
421 xpc_object_t xpcString = xpc_string_create(*ptr++);
422 xpc_array_append_value(envArray, xpcString);
423 xpc_release(xpcString);
424 }
425 xpc_dictionary_set_value(message, PARAM_ENV, envArray);
426 xpc_release(envArray);
427 }
428
429 xpc_dictionary_set_string(message, PARAM_TOOL_PATH, pathToTool);
430 xpc_dictionary_set_uint64(message, PARAM_EUID, uid);
431 {
432 const char *cwd = getcwd(NULL, 0);
433 if (cwd) {
434 xpc_dictionary_set_string(message, PARAM_CWD, cwd);
435 }
436 }
437 xpc_dictionary_set_bool(message, PARAM_CHILDEND_NEEDED, waitForEndNeeded);
438
439 if (arguments) {
440 xpc_object_t paramsArray = xpc_array_create(NULL, 0);
441 int i = 0;
442 while (arguments[i] != NULL) {
443 xpc_object_t xpcString = xpc_string_create(arguments[i++]);
444 xpc_array_append_value(paramsArray, xpcString);
445 xpc_release(xpcString);
446 }
447 xpc_dictionary_set_value(message, PARAM_TOOL_PARAMS, paramsArray);
448 xpc_release(paramsArray);
449 }
450 xpc_dictionary_set_data(message, PARAM_AUTHREF, extAuthorization, sizeof(*extAuthorization));
451
452 retval = errAuthorizationToolExecuteFailure;
453 if (trampolineConnection) {
454 xpc_connection_send_message_with_reply(trampolineConnection, message, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(xpc_object_t event) {
455 xpc_type_t type = xpc_get_type(event);
456 const char *requestId = xpc_dictionary_get_string(event, XPC_REQUEST_ID);
457 if (type == XPC_TYPE_ERROR) {
458 os_log_error(AUTH_LOG, "Error when trying to communicate with the trampoline");
459 }
460 else if (requestId && strncmp(XPC_REPLY_MSG, requestId, strlen(XPC_REPLY_MSG)) == 0) {
461 retval = (OSStatus)xpc_dictionary_get_int64(event, RETVAL_STATUS);
462 if (newProcessPid && retval == errAuthorizationSuccess) {
463 *newProcessPid = (OSStatus)xpc_dictionary_get_uint64(event, RETVAL_CHILD_PID);
464 }
465 } else {
466 os_log_error(AUTH_LOG, "Trampoline returned invalid data");
467 }
468 dispatch_semaphore_signal(sema);
469 });
470 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
471 } else {
472 os_log_error(AUTH_LOG, "Unable to establish connection to the trampoline");
473 }
474 dispatch_release(sema);
475
476 finish:
477 if (message) {
478 xpc_release(message);
479 }
480 return retval;
481 }