1/*
2 * Copyright (c) 2000-2004 Apple Computer, 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