]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
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> | |
427c49bc A |
36 | #include <Security/AuthorizationPriv.h> |
37 | #include <Security/SecBase.h> | |
b1ab9ed8 A |
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 | ||
427c49bc A |
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 | ||
b1ab9ed8 A |
80 | // |
81 | // The public client API function. | |
82 | // | |
427c49bc | 83 | OSStatus AuthorizationExecuteWithPrivilegesExternalForm(const AuthorizationExternalForm * extForm, |
b1ab9ed8 A |
84 | const char *pathToTool, |
85 | AuthorizationFlags flags, | |
86 | char *const *arguments, | |
87 | FILE **communicationsPipe) | |
88 | { | |
427c49bc A |
89 | if (extForm == NULL) |
90 | return errAuthorizationInvalidPointer; | |
91 | ||
b1ab9ed8 A |
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 | ||
b1ab9ed8 A |
103 | // create the mailbox file |
104 | FILE *mbox = tmpfile(); | |
105 | if (!mbox) | |
106 | return errAuthorizationInternal; | |
427c49bc | 107 | if (fwrite(extForm, sizeof(*extForm), 1, mbox) != 1) { |
b1ab9ed8 A |
108 | fclose(mbox); |
109 | return errAuthorizationInternal; | |
110 | } | |
111 | fflush(mbox); | |
112 | ||
427c49bc A |
113 | // compute the argument vector here because we can't allocate memory once we fork. |
114 | ||
b1ab9ed8 A |
115 | // make text representation of the temp-file descriptor |
116 | char mboxFdText[20]; | |
117 | snprintf(mboxFdText, sizeof(mboxFdText), "auth %d", fileno(mbox)); | |
427c49bc A |
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 | ||
b1ab9ed8 A |
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 | ||
427c49bc A |
145 | OSStatus status = errSecSuccess; |
146 | ||
b1ab9ed8 A |
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 | |
b1ab9ed8 A |
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]); } | |
427c49bc | 186 | goto exit_point; |
b1ab9ed8 A |
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)"); | |
427c49bc A |
192 | status = errSecSuccess; |
193 | goto exit_point; | |
b1ab9ed8 A |
194 | } |
195 | } | |
427c49bc A |
196 | break; |
197 | ||
b1ab9ed8 A |
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 | ||
b1ab9ed8 | 217 | // okay, execute the trampoline |
427c49bc | 218 | if (argv) |
b1ab9ed8 | 219 | execv(trampoline, (char *const*)argv); |
b1ab9ed8 A |
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 | } | |
427c49bc A |
230 | |
231 | exit_point: | |
232 | free(argv); | |
233 | return status; | |
b1ab9ed8 A |
234 | } |
235 | ||
236 | ||
237 | // | |
238 | // Build an argv vector | |
239 | // | |
240 | static 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 | } |