1 | /* |
---|
2 | * Copyright (c) 1992-1996 Michael A. Cooper. |
---|
3 | * This software may be freely used and distributed provided it is not sold |
---|
4 | * for profit or used for commercial gain and the author is credited |
---|
5 | * appropriately. |
---|
6 | */ |
---|
7 | |
---|
8 | #ifndef lint |
---|
9 | static char *RCSid = "$Id: run.c,v 1.1.1.2 1998-02-12 21:32:01 ghudson Exp $"; |
---|
10 | #endif |
---|
11 | |
---|
12 | /* |
---|
13 | * Things related to running system commands. |
---|
14 | */ |
---|
15 | |
---|
16 | #include "defs.h" |
---|
17 | |
---|
18 | /* |
---|
19 | * Need default environment for some OS's like HP-UX. |
---|
20 | */ |
---|
21 | static char *DefEnviron[] = { |
---|
22 | "HOME=/dev/null", |
---|
23 | NULL }; |
---|
24 | extern char **Environ; |
---|
25 | uid_t SavedUserID; |
---|
26 | |
---|
27 | /* |
---|
28 | * Set our User ID. |
---|
29 | */ |
---|
30 | int SetUserID(RealUID, EffectUID) |
---|
31 | uid_t RealUID; |
---|
32 | uid_t EffectUID; |
---|
33 | { |
---|
34 | if (Debug) printf("SetUserID(%d, %d) current: ruid=%d euid=%d\n", |
---|
35 | RealUID, EffectUID, getuid(), geteuid()); |
---|
36 | |
---|
37 | if (setreuid(RealUID, EffectUID) == -1) { |
---|
38 | if (Debug) Error("setreuid to %d, %d failed: %s", |
---|
39 | RealUID, EffectUID, SYSERR); |
---|
40 | return(-1); |
---|
41 | } |
---|
42 | |
---|
43 | if (Debug) printf("SetUserID(%d, %d) new: ruid=%d euid=%d\n", |
---|
44 | RealUID, EffectUID, getuid(), geteuid()); |
---|
45 | |
---|
46 | return(0); |
---|
47 | } |
---|
48 | |
---|
49 | /* |
---|
50 | * Set environment variable "Key" to be "Value". |
---|
51 | */ |
---|
52 | int SetEnv(Key, Value) |
---|
53 | char *Key; |
---|
54 | char *Value; |
---|
55 | { |
---|
56 | static char Buff[BUFSIZ]; |
---|
57 | |
---|
58 | (void) sprintf(Buff, "%s=%s", Key, (Value) ? Value : ""); |
---|
59 | if (putenv(strdup(Buff)) != 0) { |
---|
60 | if (Debug) Error("putenv(%s) failed.", Buff); |
---|
61 | return(-1); |
---|
62 | } |
---|
63 | |
---|
64 | return(0); |
---|
65 | } |
---|
66 | |
---|
67 | /* |
---|
68 | * Initialize environment before executing external command. |
---|
69 | */ |
---|
70 | int ExecInit(WithPrivs) |
---|
71 | int WithPrivs; |
---|
72 | { |
---|
73 | static int First = TRUE; |
---|
74 | register char **PtrPtr; |
---|
75 | |
---|
76 | if (First) { |
---|
77 | First = FALSE; |
---|
78 | SavedUserID = (uid_t) -1; |
---|
79 | /* |
---|
80 | * Remove environment variables considered to be a security risk. |
---|
81 | */ |
---|
82 | for (PtrPtr = Environ; PtrPtr && *PtrPtr; ++PtrPtr) { |
---|
83 | if (EQN(*PtrPtr, "IFS=", 4)) { |
---|
84 | if (SetEnv("IFS", NULL) < 0) |
---|
85 | return(-1); |
---|
86 | } else if (EQN(*PtrPtr, "LD_", 3)) { |
---|
87 | if (SetEnv(*PtrPtr, NULL) < 0) |
---|
88 | return(-1); |
---|
89 | } |
---|
90 | } |
---|
91 | } |
---|
92 | |
---|
93 | /* |
---|
94 | * Only change user ID if we're setuid root (uid==0). |
---|
95 | */ |
---|
96 | if (!WithPrivs && (geteuid() == 0) && ((SavedUserID = getuid()) != 0)) |
---|
97 | if (SetUserID(0, SavedUserID) == -1) |
---|
98 | return(-1); |
---|
99 | |
---|
100 | return(0); |
---|
101 | } |
---|
102 | |
---|
103 | /* |
---|
104 | * Reset things after executing external command. |
---|
105 | */ |
---|
106 | int ExecEnd(WithPrivs) |
---|
107 | int WithPrivs; |
---|
108 | { |
---|
109 | if (SavedUserID != (uid_t)-1 && SetUserID(SavedUserID, 0) == -1) |
---|
110 | return(-1); |
---|
111 | return(0); |
---|
112 | } |
---|
113 | |
---|
114 | /* |
---|
115 | * Run a list of commands (found in cmds) and return command output. |
---|
116 | */ |
---|
117 | extern char *RunCmds(Cmds, WithPrivs) |
---|
118 | char **Cmds; |
---|
119 | int WithPrivs; |
---|
120 | { |
---|
121 | static char Buf[BUFSIZ]; |
---|
122 | int l; |
---|
123 | int Done = 0; |
---|
124 | FILE *pf; |
---|
125 | register char *p; |
---|
126 | char **Cmd; |
---|
127 | |
---|
128 | if (ExecInit(WithPrivs) != 0) |
---|
129 | return((char *)NULL); |
---|
130 | |
---|
131 | Buf[0] = C_NULL; |
---|
132 | for (Cmd = Cmds; Cmd != NULL && *Cmd != NULL && !Done; ++Cmd) { |
---|
133 | /* |
---|
134 | * If this command has any args, nuke them for the access() test. |
---|
135 | */ |
---|
136 | strcpy(Buf, *Cmd); |
---|
137 | p = strchr(Buf, ' '); |
---|
138 | if (p != NULL) |
---|
139 | *p = C_NULL; |
---|
140 | |
---|
141 | if (access(Buf, X_OK) != 0) |
---|
142 | continue; |
---|
143 | |
---|
144 | if (Debug) printf("RunCmd '%s' %s Privs\n", |
---|
145 | *Cmd, (WithPrivs) ? "With" : "Without"); |
---|
146 | |
---|
147 | if ((pf = popen(*Cmd, "r")) == NULL) |
---|
148 | continue; |
---|
149 | if (fgets(Buf, sizeof(Buf), pf) == NULL) { |
---|
150 | pclose(pf); |
---|
151 | continue; |
---|
152 | } |
---|
153 | pclose(pf); |
---|
154 | |
---|
155 | l = strlen(Buf); |
---|
156 | if (Buf[l-1] == '\n') |
---|
157 | Buf[l-1] = C_NULL; |
---|
158 | |
---|
159 | Done = TRUE; |
---|
160 | } |
---|
161 | |
---|
162 | if (ExecEnd(WithPrivs) != 0) |
---|
163 | return((char *)NULL); |
---|
164 | |
---|
165 | return((Buf[0]) ? Buf : (char *)NULL); |
---|
166 | } |
---|
167 | |
---|
168 | /* |
---|
169 | * Wait for a given process to exit and return |
---|
170 | * that processes exit status. |
---|
171 | */ |
---|
172 | #if WAIT_TYPE == WAIT_WAITPID |
---|
173 | int WaitForProc(ProcID) |
---|
174 | pid_t ProcID; |
---|
175 | { |
---|
176 | pid_t RetProcID; |
---|
177 | waitarg_t ProcStatus; |
---|
178 | |
---|
179 | RetProcID = waitpid(ProcID, &ProcStatus, 0); |
---|
180 | |
---|
181 | if (RetProcID == ProcID) |
---|
182 | if (WIFEXITED(ProcStatus)) |
---|
183 | return(WAITEXITSTATUS(ProcStatus)); |
---|
184 | else { |
---|
185 | Error("waitpid(%d, , 0) failed and returned %d: %s.", |
---|
186 | ProcID, RetProcID, SYSERR); |
---|
187 | return(-1); |
---|
188 | } |
---|
189 | else |
---|
190 | return(-1); |
---|
191 | } |
---|
192 | #endif /* WAIT_WAITPID */ |
---|
193 | #if WAIT_TYPE == WAIT_WAIT4 |
---|
194 | int WaitForProc(ProcID) |
---|
195 | pid_t ProcID; |
---|
196 | { |
---|
197 | pid_t RetProcID; |
---|
198 | waitarg_t ProcStatus; |
---|
199 | |
---|
200 | RetProcID = wait4(ProcID, &ProcStatus, 0, NULL); |
---|
201 | |
---|
202 | if (RetProcID == ProcID) |
---|
203 | if (WIFEXITED(ProcStatus)) |
---|
204 | return(WAITEXITSTATUS(ProcStatus)); |
---|
205 | else { |
---|
206 | Error("wait4(%d) failed and returned %d: %s.", |
---|
207 | ProcID, RetProcID, SYSERR); |
---|
208 | return(-1); |
---|
209 | } |
---|
210 | else |
---|
211 | return(-1); |
---|
212 | } |
---|
213 | #endif /* WAIT_WAIT4 */ |
---|
214 | |
---|
215 | /* |
---|
216 | * Execute a command with given arguments. |
---|
217 | */ |
---|
218 | int Execute(Cmd, Argv, Env, WithPrivs, StdOut, StdErr) |
---|
219 | char *Cmd; |
---|
220 | char **Argv; |
---|
221 | char **Env; |
---|
222 | int WithPrivs; |
---|
223 | int StdOut; |
---|
224 | int StdErr; |
---|
225 | { |
---|
226 | pid_t ProcID = 0; |
---|
227 | int Status; |
---|
228 | register char **PtrPtr; |
---|
229 | |
---|
230 | if (access(Cmd, X_OK) != 0) |
---|
231 | return(-1); |
---|
232 | |
---|
233 | if (!Env) |
---|
234 | Env = DefEnviron; |
---|
235 | |
---|
236 | if (Debug) { |
---|
237 | printf("Execute '%s'", Cmd); |
---|
238 | for (PtrPtr = Argv; PtrPtr && *PtrPtr; ++PtrPtr) |
---|
239 | printf(" '%s'", *PtrPtr); |
---|
240 | printf("\t%s Privs\n", (WithPrivs) ? "With" : "Without"); |
---|
241 | } |
---|
242 | |
---|
243 | ProcID = fork(); |
---|
244 | if (ProcID < 0) { |
---|
245 | Error("Fork failed: %s", SYSERR); |
---|
246 | return(-1); |
---|
247 | } else if (ProcID == 0) { |
---|
248 | /* |
---|
249 | * Child |
---|
250 | */ |
---|
251 | if (StdOut >= 0) |
---|
252 | if (dup2(StdOut, fileno(stdout)) < 0) |
---|
253 | Error("dup2(%d, stdout) failed: %s.", StdOut); |
---|
254 | if (StdErr >= 0) |
---|
255 | if (dup2(StdErr, fileno(stderr)) < 0) |
---|
256 | Error("dup2(%d, stderr) failed: %s.", StdErr); |
---|
257 | ExecInit(WithPrivs); |
---|
258 | execve(Cmd, Argv, Env); |
---|
259 | Error("Execve \"%s\" failed: %s", Cmd, SYSERR); |
---|
260 | exit(127); |
---|
261 | } else { |
---|
262 | /* |
---|
263 | * Parent |
---|
264 | */ |
---|
265 | Status = WaitForProc(ProcID); |
---|
266 | if (Debug) printf("\tCommand '%s' exited %d.\n", Cmd, Status); |
---|
267 | return(Status); |
---|
268 | } |
---|
269 | return(-1); |
---|
270 | } |
---|
271 | |
---|
272 | #if defined(RUN_TEST_CMD) |
---|
273 | static char *RunTestCmd[] = RUN_TEST_CMD; |
---|
274 | #endif /* RUN_TEST_CMD */ |
---|
275 | |
---|
276 | /* |
---|
277 | * Get the Argument Vector for the command to run. |
---|
278 | */ |
---|
279 | static char **GetRunArgv(Command) |
---|
280 | char *Command; |
---|
281 | { |
---|
282 | static char **Argv = NULL; |
---|
283 | char *Base; |
---|
284 | #if defined(RUN_TEST_CMD) |
---|
285 | register char **ArgvPtr; |
---|
286 | register char **PtrPtr; |
---|
287 | register int Count; |
---|
288 | |
---|
289 | for (Count = 0, PtrPtr = RunTestCmd; PtrPtr && *PtrPtr; ++PtrPtr, ++Count); |
---|
290 | |
---|
291 | if (Argv) |
---|
292 | (void) free(Argv); |
---|
293 | ArgvPtr = Argv = (char **) xmalloc((Count+2) * sizeof(char *)); |
---|
294 | |
---|
295 | for (PtrPtr = RunTestCmd; PtrPtr && *PtrPtr; ++PtrPtr, ++ArgvPtr) |
---|
296 | *ArgvPtr = *PtrPtr; |
---|
297 | *ArgvPtr = Command; |
---|
298 | *++ArgvPtr = NULL; |
---|
299 | #else /* !RUN_TEST_CMD */ |
---|
300 | Base = strrchr(Command, '/'); |
---|
301 | if (Base) |
---|
302 | ++Base; |
---|
303 | else |
---|
304 | Base = Command; |
---|
305 | if (Argv) |
---|
306 | (void) free(Argv); |
---|
307 | Argv = (char **) xmalloc(4 * sizeof(char *)); |
---|
308 | Argv[0] = Command; |
---|
309 | Argv[1] = Base; |
---|
310 | Argv[2] = NULL; |
---|
311 | #endif /* RUN_TEST_CMD */ |
---|
312 | |
---|
313 | return(Argv); |
---|
314 | } |
---|
315 | |
---|
316 | |
---|
317 | /* |
---|
318 | * Run a list of test files. Each test file is run and if the |
---|
319 | * exit status is 0, we return the basename of the command. |
---|
320 | * e.g. If "/bin/vax" exists and returns status 0, return string "vax". |
---|
321 | */ |
---|
322 | extern char *RunTestFiles(Cmds) |
---|
323 | char **Cmds; |
---|
324 | { |
---|
325 | char **Cmd; |
---|
326 | char **RunEnv; |
---|
327 | char **Argv; |
---|
328 | char *Name = NULL; |
---|
329 | register char *p; |
---|
330 | static char Buf[BUFSIZ]; |
---|
331 | int StdOut = -1; |
---|
332 | int StdErr = -1; |
---|
333 | |
---|
334 | /* |
---|
335 | * Setup stdout/stderr to go to /dev/null since we |
---|
336 | * only care about the exit status of commands. |
---|
337 | */ |
---|
338 | if (!Debug) { |
---|
339 | StdOut = open(_PATH_NULL, O_WRONLY); |
---|
340 | StdErr = open(_PATH_NULL, O_WRONLY); |
---|
341 | } |
---|
342 | |
---|
343 | for (Cmd = Cmds; Name == NULL && Cmd != NULL && *Cmd != NULL; ++Cmd) { |
---|
344 | /* |
---|
345 | * If this command has any args, nuke them for the access() test. |
---|
346 | */ |
---|
347 | strcpy(Buf, *Cmd); |
---|
348 | p = strchr(Buf, ' '); |
---|
349 | if (p != NULL) |
---|
350 | *p = C_NULL; |
---|
351 | |
---|
352 | if (access(Buf, X_OK) != 0) |
---|
353 | continue; |
---|
354 | |
---|
355 | /* |
---|
356 | * Execute the command with a NULL environment for security |
---|
357 | * reasons. |
---|
358 | */ |
---|
359 | Argv = GetRunArgv(*Cmd); |
---|
360 | if (Execute(Argv[0], &Argv[1], (char **)NULL, 0, StdOut, StdErr) != 0) |
---|
361 | continue; |
---|
362 | |
---|
363 | /* |
---|
364 | * The name of this architecture is the last part of the Cmd name. |
---|
365 | */ |
---|
366 | strcpy(Buf, *Cmd); |
---|
367 | p = strrchr(Buf, '/'); |
---|
368 | if (p != NULL) |
---|
369 | ++p; |
---|
370 | Name = p; |
---|
371 | } |
---|
372 | |
---|
373 | if (StdOut >= 0) |
---|
374 | (void) close(StdOut); |
---|
375 | if (StdErr >= 0) |
---|
376 | (void) close(StdErr); |
---|
377 | |
---|
378 | return(Name); |
---|
379 | } |
---|