2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // trampolineClient - Authorization trampoline client-side implementation
28 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <Security/SecBase.h>
35 #include <security_utilities/endian.h>
36 #include <security_utilities/debugging.h>
38 #include "Security/Authorization.h"
39 #include "AuthorizationPriv.h"
40 #include "AuthorizationTrampolinePriv.h"
41 #include <dispatch/semaphore.h>
44 #include <sys/ioctl.h>
49 // A few names for clarity's sake
52 READ
= 0, // read end of standard UNIX pipe
53 WRITE
= 1 // write end of standard UNIX pipe
56 static os_log_t
AUTH_LOG_DEFAULT() {
57 static dispatch_once_t once
;
59 dispatch_once(&once
, ^{ log
= os_log_create("com.apple.Authorization", "Trampoline"); });
63 #define AUTH_LOG AUTH_LOG_DEFAULT()
66 // Where is the trampoline itself?
68 #if !defined(TRAMPOLINE)
69 # define TRAMPOLINE "/usr/libexec/security_authtrampoline" /* fallback */
74 // Local (static) functions
76 static const char **argVector(const char *trampoline
,
77 const char *tool
, const char *commFd
,
78 char *const *arguments
);
81 OSStatus
AuthorizationExecuteWithPrivileges(AuthorizationRef authorization
,
82 const char *pathToTool
,
83 AuthorizationFlags flags
,
84 char *const *arguments
,
85 FILE **communicationsPipe
)
87 // externalize the authorization
88 AuthorizationExternalForm extForm
;
89 if (OSStatus err
= AuthorizationMakeExternalForm(authorization
, &extForm
))
92 return AuthorizationExecuteWithPrivilegesExternalForm(&extForm
, pathToTool
, flags
, arguments
, communicationsPipe
);
96 // The public client API function.
98 OSStatus
AuthorizationExecuteWithPrivilegesExternalForm(const AuthorizationExternalForm
* extForm
,
99 const char *pathToTool
,
100 AuthorizationFlags flags
,
101 char *const *arguments
,
102 FILE **communicationsPipe
)
104 os_log(AUTH_LOG
, "AuthorizationExecuteWithPrivileges and AuthorizationExecuteWithPrivilegesExternalForm are deprecated and functionality will be removed soon - please update your application");
106 return errAuthorizationInvalidPointer
;
108 // flags are currently reserved
110 return errAuthorizationInvalidFlags
;
112 // compute the argument vector here because we can't allocate memory once we fork.
114 // where is the trampoline?
116 const char *trampoline
= TRAMPOLINE
;
118 const char *trampoline
= getenv("AUTHORIZATIONTRAMPOLINE");
120 trampoline
= TRAMPOLINE
;
123 // make a data exchange pipe
125 if (pipe(dataPipe
)) {
126 os_log_error(AUTH_LOG
, "data pipe failure");
127 return errAuthorizationToolExecuteFailure
;
130 // make text representation of the pipe handle
132 snprintf(pipeFdText
, sizeof(pipeFdText
), "auth %d", dataPipe
[READ
]);
133 const char **argv
= argVector(trampoline
, pathToTool
, pipeFdText
, arguments
);
135 // make a notifier pipe
138 close(dataPipe
[READ
]); close(dataPipe
[WRITE
]);
142 os_log_error(AUTH_LOG
, "notify pipe failure");
143 return errAuthorizationToolExecuteFailure
;
146 // make the communications pipe if requested
148 if (communicationsPipe
&& socketpair(AF_UNIX
, SOCK_STREAM
, 0, comm
)) {
149 close(notify
[READ
]); close(notify
[WRITE
]);
150 close(dataPipe
[READ
]); close(dataPipe
[WRITE
]);
154 os_log_error(AUTH_LOG
, "comm pipe failure");
155 return errAuthorizationToolExecuteFailure
;
158 OSStatus status
= errSecSuccess
;
160 // do the standard forking tango...
162 for (int n
= 5;; n
--, delay
*= 2) {
165 if (errno
== EAGAIN
) {
166 // potentially recoverable resource shortage
168 os_log(AUTH_LOG
, "resource shortage (EAGAIN), delaying %d seconds", delay
);
173 os_log_error(AUTH_LOG
, "fork failed (errno=%d)", errno
);
174 close(notify
[READ
]); close(notify
[WRITE
]);
175 status
= errAuthorizationToolExecuteFailure
;
179 // close foreign side of pipes
180 close(notify
[WRITE
]);
181 if (communicationsPipe
)
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
;
189 close(dataPipe
[WRITE
]);
190 if (communicationsPipe
) {
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
);
200 default: // weird result of read: post error
201 os_log_error(AUTH_LOG
, "unexpected read return value %ld", long(rc
));
202 status
= errAuthorizationToolEnvironmentError
;
204 case sizeof(status
): // read succeeded: child reported an error
205 os_log_error(AUTH_LOG
, "parent received status=%d", (int)status
);
207 close(dataPipe
[WRITE
]);
208 if (communicationsPipe
) {
213 case 0: // end of file: exec succeeded
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
;
225 // close foreign side of pipes
227 if (communicationsPipe
)
230 // close write end of the data PIPE
231 close(dataPipe
[WRITE
]);
233 // fd 1 (stdout) holds the notify write end
234 dup2(notify
[WRITE
], 1);
235 close(notify
[WRITE
]);
237 // fd 0 (stdin) holds either the comm-link write-end or /dev/null
238 if (communicationsPipe
) {
239 dup2(comm
[WRITE
], 0);
243 open("/dev/null", O_RDWR
);
246 // okay, execute the trampoline
248 execv(trampoline
, (char *const*)argv
);
250 // execute failed - tell the parent
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
;
257 write(1, &error
, sizeof(error
));
270 // Build an argv vector
272 static const char **argVector(const char *trampoline
, const char *pathToTool
,
273 const char *mboxFdText
, char *const *arguments
)
277 for (char *const *p
= arguments
; *p
; p
++)
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
;
285 for (int n
= 0; arguments
[n
]; n
++)
286 args
[n
+ 3] = arguments
[n
];
287 args
[length
+ 3] = NULL
;
295 OSStatus
AuthorizationExecuteWithPrivilegesInternal(const AuthorizationRef authorization
,
296 const char * _Nonnull pathToTool
,
297 const char * _Nonnull
const * arguments
,
298 pid_t
* newProcessPid
,
303 void(^processFinished
)(const int exitStatus
))
305 // externalize the authorization
306 AuthorizationExternalForm extForm
;
307 if (OSStatus err
= AuthorizationMakeExternalForm(authorization
, &extForm
))
310 return AuthorizationExecuteWithPrivilegesExternalFormInternal(&extForm
, pathToTool
, arguments
, newProcessPid
, uid
, stdOut
, stdErr
, stdIn
, processFinished
);
313 OSStatus
AuthorizationExecuteWithPrivilegesExternalFormInternal(const AuthorizationExternalForm
*extAuthorization
,
314 const char * _Nonnull pathToTool
,
315 const char * _Nullable
const * _Nullable arguments
,
316 pid_t
* newProcessPid
,
321 void(^processFinished
)(const int exitStatus
))
323 xpc_object_t message
;
324 __block OSStatus retval
= errAuthorizationInternal
;
325 dispatch_semaphore_t sema
= dispatch_semaphore_create(0);
327 os_log_error(AUTH_LOG
, "Unable to create trampoline semaphore");
330 __block xpc_connection_t trampolineConnection
= xpc_connection_create_mach_service("com.apple.security.authtrampoline", NULL
, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
);
332 if (!trampolineConnection
) {
333 os_log_error(AUTH_LOG
, "Unable to create trampoline mach service");
334 dispatch_release(sema
);
338 xpc_connection_set_event_handler(trampolineConnection
, ^(xpc_object_t event
) {
339 xpc_type_t type
= xpc_get_type(event
);
341 if (type
== XPC_TYPE_ERROR
) {
342 if (trampolineConnection
) {
343 xpc_release(trampolineConnection
);
344 trampolineConnection
= NULL
;
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
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
);
358 if (trampolineConnection
) {
359 xpc_connection_cancel(trampolineConnection
);
360 xpc_release(trampolineConnection
);
361 trampolineConnection
= NULL
;
363 if (processFinished
) {
364 processFinished(exitStatus
);
367 os_log_error(AUTH_LOG
, "Unknown event type [%s] arrived from trampoline", eventType
);
370 os_log_error(AUTH_LOG
, "Unknown request [%s] arrived from trampoline", requestId
);
375 xpc_connection_resume(trampolineConnection
);
377 message
= xpc_dictionary_create(NULL
, NULL
, 0);
378 xpc_dictionary_set_string(message
, XPC_REQUEST_ID
, XPC_REQUEST_CREATE_PROCESS
);
380 Boolean waitForEndNeeded
= (processFinished
!= NULL
);
382 xpc_object_t xpcInFd
= xpc_fd_create(stdIn
);
384 os_log_error(AUTH_LOG
, "Unable to create XPC stdin FD");
387 xpc_dictionary_set_value(message
, PARAM_STDIN
, xpcInFd
);
388 xpc_release(xpcInFd
);
389 waitForEndNeeded
= true;
393 xpc_object_t xpcOutFd
= xpc_fd_create(stdOut
);
395 os_log_error(AUTH_LOG
, "Unable to create XPC stdout FD");
398 xpc_dictionary_set_value(message
, PARAM_STDOUT
, xpcOutFd
);
399 xpc_release(xpcOutFd
);
400 waitForEndNeeded
= true;
404 xpc_object_t xpcErrFd
= xpc_fd_create(stdErr
);
406 os_log_error(AUTH_LOG
, "Unable to create XPC stderr FD");
409 xpc_dictionary_set_value(message
, PARAM_STDERR
, xpcErrFd
);
410 xpc_release(xpcErrFd
);
411 waitForEndNeeded
= true;
414 extern char** environ
;
417 xpc_object_t envArray
= xpc_array_create(NULL
, 0);
418 char **ptr
= environ
;
421 xpc_object_t xpcString
= xpc_string_create(*ptr
++);
422 xpc_array_append_value(envArray
, xpcString
);
423 xpc_release(xpcString
);
425 xpc_dictionary_set_value(message
, PARAM_ENV
, envArray
);
426 xpc_release(envArray
);
429 xpc_dictionary_set_string(message
, PARAM_TOOL_PATH
, pathToTool
);
430 xpc_dictionary_set_uint64(message
, PARAM_EUID
, uid
);
432 const char *cwd
= getcwd(NULL
, 0);
434 xpc_dictionary_set_string(message
, PARAM_CWD
, cwd
);
437 xpc_dictionary_set_bool(message
, PARAM_CHILDEND_NEEDED
, waitForEndNeeded
);
440 xpc_object_t paramsArray
= xpc_array_create(NULL
, 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
);
447 xpc_dictionary_set_value(message
, PARAM_TOOL_PARAMS
, paramsArray
);
448 xpc_release(paramsArray
);
450 xpc_dictionary_set_data(message
, PARAM_AUTHREF
, extAuthorization
, sizeof(*extAuthorization
));
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");
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
);
466 os_log_error(AUTH_LOG
, "Trampoline returned invalid data");
468 dispatch_semaphore_signal(sema
);
470 dispatch_semaphore_wait(sema
, DISPATCH_TIME_FOREVER
);
472 os_log_error(AUTH_LOG
, "Unable to establish connection to the trampoline");
474 dispatch_release(sema
);
478 xpc_release(message
);