]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_authorization/lib/trampolineClient.cpp
Security-58286.200.222.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/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 //
52 enum {
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 //
61 static const char **argVector(const char *trampoline,
62 const char *tool, const char *commFd,
63 char *const *arguments);
64
65
66 OSStatus 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 //
83 OSStatus 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 // compute the argument vector here because we can't allocate memory once we fork.
104
105 // where is the trampoline?
106 #if defined(NDEBUG)
107 const char *trampoline = TRAMPOLINE;
108 #else //!NDEBUG
109 const char *trampoline = getenv("AUTHORIZATIONTRAMPOLINE");
110 if (!trampoline)
111 trampoline = TRAMPOLINE;
112 #endif //NDEBUG
113
114 // make a data exchange pipe
115 int dataPipe[2];
116 if (pipe(dataPipe)) {
117 secinfo("authexec", "data pipe failure");
118 return errAuthorizationToolExecuteFailure;
119 }
120
121 // make text representation of the pipe handle
122 char pipeFdText[20];
123 snprintf(pipeFdText, sizeof(pipeFdText), "auth %d", dataPipe[READ]);
124 const char **argv = argVector(trampoline, pathToTool, pipeFdText, arguments);
125
126 // make a notifier pipe
127 int notify[2];
128 if (pipe(notify)) {
129 close(dataPipe[READ]); close(dataPipe[WRITE]);
130 if(argv) {
131 free(argv);
132 }
133 secinfo("authexec", "notify pipe failure");
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 close(dataPipe[READ]); close(dataPipe[WRITE]);
142 if(argv) {
143 free(argv);
144 }
145 secinfo("authexec", "comm pipe failure");
146 return errAuthorizationToolExecuteFailure;
147 }
148
149 OSStatus status = errSecSuccess;
150
151 // do the standard forking tango...
152 int delay = 1;
153 for (int n = 5;; n--, delay *= 2) {
154 switch (fork()) {
155 case -1: // error
156 if (errno == EAGAIN) {
157 // potentially recoverable resource shortage
158 if (n > 0) {
159 secinfo("authexec", "resource shortage (EAGAIN), delaying %d seconds", delay);
160 sleep(delay);
161 continue;
162 }
163 }
164 secinfo("authexec", "fork failed (errno=%d)", errno);
165 close(notify[READ]); close(notify[WRITE]);
166 status = errAuthorizationToolExecuteFailure;
167 goto exit_point;
168
169 default: { // parent
170 // close foreign side of pipes
171 close(notify[WRITE]);
172 if (communicationsPipe)
173 close(comm[WRITE]);
174
175 close(dataPipe[READ]);
176 if (write(dataPipe[WRITE], extForm, sizeof(*extForm)) != sizeof(*extForm)) {
177 secinfo("authexec", "fwrite data failed (errno=%d)", errno);
178 status = errAuthorizationInternal;
179 close(notify[READ]);
180 close(dataPipe[WRITE]);
181 if (communicationsPipe) {
182 close(comm[READ]);
183 close(comm[WRITE]);
184 }
185 goto exit_point;
186 }
187 close(dataPipe[WRITE]);
188 // get status notification from child
189 secinfo("authexec", "parent waiting for status");
190 ssize_t rc = read(notify[READ], &status, sizeof(status));
191 status = n2h(status);
192 switch (rc) {
193 default: // weird result of read: post error
194 secinfo("authexec", "unexpected read return value %ld", long(rc));
195 status = errAuthorizationToolEnvironmentError;
196 // fall through
197 case sizeof(status): // read succeeded: child reported an error
198 secinfo("authexec", "parent received status=%d", (int)status);
199 close(notify[READ]);
200 close(dataPipe[WRITE]);
201 if (communicationsPipe) {
202 close(comm[READ]);
203 close(comm[WRITE]);
204 }
205 goto exit_point;
206 case 0: // end of file: exec succeeded
207 close(notify[READ]);
208 close(dataPipe[WRITE]);
209 if (communicationsPipe)
210 *communicationsPipe = fdopen(comm[READ], "r+");
211 secinfo("authexec", "parent resumes (no error)");
212 status = errSecSuccess;
213 goto exit_point;
214 }
215 }
216
217 case 0: // child
218 // close foreign side of pipes
219 close(notify[READ]);
220 if (communicationsPipe)
221 close(comm[READ]);
222
223 // close write end of the data PIPE
224 close(dataPipe[WRITE]);
225
226 // fd 1 (stdout) holds the notify write end
227 dup2(notify[WRITE], 1);
228 close(notify[WRITE]);
229
230 // fd 0 (stdin) holds either the comm-link write-end or /dev/null
231 if (communicationsPipe) {
232 dup2(comm[WRITE], 0);
233 close(comm[WRITE]);
234 } else {
235 close(0);
236 open("/dev/null", O_RDWR);
237 }
238
239 // okay, execute the trampoline
240 if (argv)
241 execv(trampoline, (char *const*)argv);
242
243 // execute failed - tell the parent
244 {
245 // in case of failure, close read end of the data pipe as well
246 close(dataPipe[WRITE]);
247 close(dataPipe[READ]);
248 OSStatus error = errAuthorizationToolExecuteFailure;
249 error = h2n(error);
250 write(1, &error, sizeof(error));
251 _exit(1);
252 }
253 }
254 }
255
256 exit_point:
257 free(argv);
258 return status;
259 }
260
261
262 //
263 // Build an argv vector
264 //
265 static const char **argVector(const char *trampoline, const char *pathToTool,
266 const char *mboxFdText, char *const *arguments)
267 {
268 int length = 0;
269 if (arguments) {
270 for (char *const *p = arguments; *p; p++)
271 length++;
272 }
273 if (const char **args = (const char **)malloc(sizeof(const char *) * (length + 4))) {
274 args[0] = trampoline;
275 args[1] = pathToTool;
276 args[2] = mboxFdText;
277 if (arguments)
278 for (int n = 0; arguments[n]; n++)
279 args[n + 3] = arguments[n];
280 args[length + 3] = NULL;
281 return args;
282 }
283 return NULL;
284 }