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
29 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <Security/SecBase.h>
36 #include <security_utilities/endian.h>
37 #include <security_utilities/debugging.h>
39 #include "Security/Authorization.h"
40 #include "AuthorizationPriv.h"
41 #include "AuthorizationTrampolinePriv.h"
42 #include <dispatch/semaphore.h>
45 #include <sys/ioctl.h>
50 // A few names for clarity's sake
53 READ
= 0, // read end of standard UNIX pipe
54 WRITE
= 1 // write end of standard UNIX pipe
57 static os_log_t
AUTH_LOG_DEFAULT() {
58 static dispatch_once_t once
;
60 dispatch_once(&once
, ^{ log
= os_log_create("com.apple.Authorization", "Trampoline"); });
64 #define AUTH_LOG AUTH_LOG_DEFAULT()
67 // Where is the trampoline itself?
69 #if !defined(TRAMPOLINE)
70 # define TRAMPOLINE "/usr/libexec/security_authtrampoline" /* fallback */
75 // Local (static) functions
77 static const char **argVector(const char *trampoline
,
78 const char *tool
, const char *commFd
,
79 char *const *arguments
);
82 OSStatus
AuthorizationExecuteWithPrivileges(AuthorizationRef authorization
,
83 const char *pathToTool
,
84 AuthorizationFlags flags
,
85 char *const *arguments
,
86 FILE **communicationsPipe
)
88 // externalize the authorization
89 AuthorizationExternalForm extForm
;
90 if (OSStatus err
= AuthorizationMakeExternalForm(authorization
, &extForm
))
93 return AuthorizationExecuteWithPrivilegesExternalForm(&extForm
, pathToTool
, flags
, arguments
, communicationsPipe
);
97 // The public client API function.
99 OSStatus
AuthorizationExecuteWithPrivilegesExternalForm(const AuthorizationExternalForm
* extForm
,
100 const char *pathToTool
,
101 AuthorizationFlags flags
,
102 char *const *arguments
,
103 FILE **communicationsPipe
)
105 os_log(AUTH_LOG
, "AuthorizationExecuteWithPrivileges and AuthorizationExecuteWithPrivilegesExternalForm are deprecated and functionality will be removed soon - please update your application");
107 return errAuthorizationInvalidPointer
;
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!");
116 // flags are currently reserved
118 return errAuthorizationInvalidFlags
;
120 // compute the argument vector here because we can't allocate memory once we fork.
122 // where is the trampoline?
124 const char *trampoline
= TRAMPOLINE
;
126 const char *trampoline
= getenv("AUTHORIZATIONTRAMPOLINE");
128 trampoline
= TRAMPOLINE
;
131 // make a data exchange pipe
133 if (pipe(dataPipe
)) {
134 os_log_error(AUTH_LOG
, "data pipe failure");
135 return errAuthorizationToolExecuteFailure
;
138 // make text representation of the pipe handle
140 snprintf(pipeFdText
, sizeof(pipeFdText
), "auth %d", dataPipe
[READ
]);
141 const char **argv
= argVector(trampoline
, pathToTool
, pipeFdText
, arguments
);
143 // make a notifier pipe
146 close(dataPipe
[READ
]); close(dataPipe
[WRITE
]);
150 os_log_error(AUTH_LOG
, "notify pipe failure");
151 return errAuthorizationToolExecuteFailure
;
154 // make the communications pipe if requested
156 if (communicationsPipe
&& socketpair(AF_UNIX
, SOCK_STREAM
, 0, comm
)) {
157 close(notify
[READ
]); close(notify
[WRITE
]);
158 close(dataPipe
[READ
]); close(dataPipe
[WRITE
]);
162 os_log_error(AUTH_LOG
, "comm pipe failure");
163 return errAuthorizationToolExecuteFailure
;
166 OSStatus status
= errSecSuccess
;
168 // do the standard forking tango...
170 for (int n
= 5;; n
--, delay
*= 2) {
173 if (errno
== EAGAIN
) {
174 // potentially recoverable resource shortage
176 os_log(AUTH_LOG
, "resource shortage (EAGAIN), delaying %d seconds", delay
);
181 os_log_error(AUTH_LOG
, "fork failed (errno=%d)", errno
);
182 close(notify
[READ
]); close(notify
[WRITE
]);
183 status
= errAuthorizationToolExecuteFailure
;
187 // close foreign side of pipes
188 close(notify
[WRITE
]);
189 if (communicationsPipe
)
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
;
197 close(dataPipe
[WRITE
]);
198 if (communicationsPipe
) {
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
);
208 default: // weird result of read: post error
209 os_log_error(AUTH_LOG
, "unexpected read return value %ld", long(rc
));
210 status
= errAuthorizationToolEnvironmentError
;
212 case sizeof(status
): // read succeeded: child reported an error
213 os_log_error(AUTH_LOG
, "parent received status=%d", (int)status
);
215 close(dataPipe
[WRITE
]);
216 if (communicationsPipe
) {
221 case 0: // end of file: exec succeeded
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
;
233 // close foreign side of pipes
235 if (communicationsPipe
)
238 // close write end of the data PIPE
239 close(dataPipe
[WRITE
]);
241 // fd 1 (stdout) holds the notify write end
242 dup2(notify
[WRITE
], 1);
243 close(notify
[WRITE
]);
245 // fd 0 (stdin) holds either the comm-link write-end or /dev/null
246 if (communicationsPipe
) {
247 dup2(comm
[WRITE
], 0);
251 open("/dev/null", O_RDWR
);
254 // okay, execute the trampoline
256 execv(trampoline
, (char *const*)argv
);
258 // execute failed - tell the parent
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
;
265 write(1, &error
, sizeof(error
));
278 // Build an argv vector
280 static const char **argVector(const char *trampoline
, const char *pathToTool
,
281 const char *mboxFdText
, char *const *arguments
)
285 for (char *const *p
= arguments
; *p
; p
++)
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
;
293 for (int n
= 0; arguments
[n
]; n
++)
294 args
[n
+ 3] = arguments
[n
];
295 args
[length
+ 3] = NULL
;
303 OSStatus
AuthorizationExecuteWithPrivilegesInternal(const AuthorizationRef authorization
,
304 const char * _Nonnull pathToTool
,
305 const char * _Nonnull
const * arguments
,
306 pid_t
* newProcessPid
,
311 void(^processFinished
)(const int exitStatus
))
313 // externalize the authorization
314 AuthorizationExternalForm extForm
;
315 if (OSStatus err
= AuthorizationMakeExternalForm(authorization
, &extForm
))
318 return AuthorizationExecuteWithPrivilegesExternalFormInternal(&extForm
, pathToTool
, arguments
, newProcessPid
, uid
, stdOut
, stdErr
, stdIn
, processFinished
);
321 OSStatus
AuthorizationExecuteWithPrivilegesExternalFormInternal(const AuthorizationExternalForm
*extAuthorization
,
322 const char * _Nonnull pathToTool
,
323 const char * _Nullable
const * _Nullable arguments
,
324 pid_t
* newProcessPid
,
329 void(^processFinished
)(const int exitStatus
))
331 xpc_object_t message
;
332 __block OSStatus retval
= errAuthorizationInternal
;
333 dispatch_semaphore_t sema
= dispatch_semaphore_create(0);
335 os_log_error(AUTH_LOG
, "Unable to create trampoline semaphore");
338 __block xpc_connection_t trampolineConnection
= xpc_connection_create_mach_service("com.apple.security.authtrampoline", NULL
, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
);
340 if (!trampolineConnection
) {
341 os_log_error(AUTH_LOG
, "Unable to create trampoline mach service");
342 dispatch_release(sema
);
346 xpc_connection_set_event_handler(trampolineConnection
, ^(xpc_object_t event
) {
347 xpc_type_t type
= xpc_get_type(event
);
349 if (type
== XPC_TYPE_ERROR
) {
350 if (trampolineConnection
) {
351 xpc_release(trampolineConnection
);
352 trampolineConnection
= NULL
;
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
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
);
366 if (trampolineConnection
) {
367 xpc_connection_cancel(trampolineConnection
);
368 xpc_release(trampolineConnection
);
369 trampolineConnection
= NULL
;
371 if (processFinished
) {
372 processFinished(exitStatus
);
375 os_log_error(AUTH_LOG
, "Unknown event type [%s] arrived from trampoline", eventType
);
378 os_log_error(AUTH_LOG
, "Unknown request [%s] arrived from trampoline", requestId
);
383 xpc_connection_resume(trampolineConnection
);
385 message
= xpc_dictionary_create(NULL
, NULL
, 0);
386 xpc_dictionary_set_string(message
, XPC_REQUEST_ID
, XPC_REQUEST_CREATE_PROCESS
);
388 Boolean waitForEndNeeded
= (processFinished
!= NULL
);
390 xpc_object_t xpcInFd
= xpc_fd_create(stdIn
);
392 os_log_error(AUTH_LOG
, "Unable to create XPC stdin FD");
395 xpc_dictionary_set_value(message
, PARAM_STDIN
, xpcInFd
);
396 xpc_release(xpcInFd
);
397 waitForEndNeeded
= true;
401 xpc_object_t xpcOutFd
= xpc_fd_create(stdOut
);
403 os_log_error(AUTH_LOG
, "Unable to create XPC stdout FD");
406 xpc_dictionary_set_value(message
, PARAM_STDOUT
, xpcOutFd
);
407 xpc_release(xpcOutFd
);
408 waitForEndNeeded
= true;
412 xpc_object_t xpcErrFd
= xpc_fd_create(stdErr
);
414 os_log_error(AUTH_LOG
, "Unable to create XPC stderr FD");
417 xpc_dictionary_set_value(message
, PARAM_STDERR
, xpcErrFd
);
418 xpc_release(xpcErrFd
);
419 waitForEndNeeded
= true;
422 extern char** environ
;
425 xpc_object_t envArray
= xpc_array_create(NULL
, 0);
426 char **ptr
= environ
;
429 xpc_object_t xpcString
= xpc_string_create(*ptr
++);
430 xpc_array_append_value(envArray
, xpcString
);
431 xpc_release(xpcString
);
433 xpc_dictionary_set_value(message
, PARAM_ENV
, envArray
);
434 xpc_release(envArray
);
437 xpc_dictionary_set_string(message
, PARAM_TOOL_PATH
, pathToTool
);
438 xpc_dictionary_set_uint64(message
, PARAM_EUID
, uid
);
440 const char *cwd
= getcwd(NULL
, 0);
442 xpc_dictionary_set_string(message
, PARAM_CWD
, cwd
);
445 xpc_dictionary_set_bool(message
, PARAM_CHILDEND_NEEDED
, waitForEndNeeded
);
448 xpc_object_t paramsArray
= xpc_array_create(NULL
, 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
);
455 xpc_dictionary_set_value(message
, PARAM_TOOL_PARAMS
, paramsArray
);
456 xpc_release(paramsArray
);
458 xpc_dictionary_set_data(message
, PARAM_AUTHREF
, extAuthorization
, sizeof(*extAuthorization
));
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");
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
);
474 os_log_error(AUTH_LOG
, "Trampoline returned invalid data");
476 dispatch_semaphore_signal(sema
);
478 dispatch_semaphore_wait(sema
, DISPATCH_TIME_FOREVER
);
480 os_log_error(AUTH_LOG
, "Unable to establish connection to the trampoline");
482 dispatch_release(sema
);
486 xpc_release(message
);