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