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