|
|
1.1 root 1: /* spawn.c
2: Spawn a program securely.
3:
4: Copyright (C) 1992 Ian Lance Taylor
5:
6: This file is part of the Taylor UUCP package.
7:
8: This program is free software; you can redistribute it and/or
9: modify it under the terms of the GNU General Public License as
10: published by the Free Software Foundation; either version 2 of the
11: License, or (at your option) any later version.
12:
13: This program is distributed in the hope that it will be useful, but
14: WITHOUT ANY WARRANTY; without even the implied warranty of
15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16: General Public License for more details.
17:
18: You should have received a copy of the GNU General Public License
19: along with this program; if not, write to the Free Software
20: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21:
22: The author of the program may be contacted at [email protected] or
23: c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
24: */
25:
26: #include "uucp.h"
27:
28: #include "uudefs.h"
29: #include "sysdep.h"
30:
31: #include <errno.h>
32:
33: #if HAVE_FCNTL_H
34: #include <fcntl.h>
35: #else
36: #if HAVE_SYS_FILE_H
37: #include <sys/file.h>
38: #endif
39: #endif
40:
41: #ifndef O_RDONLY
42: #define O_RDONLY 0
43: #define O_WRONLY 1
44: #define O_RDWR 2
45: #endif
46:
47: #ifndef FD_CLOEXEC
48: #define FD_CLOEXEC 1
49: #endif
50:
51: #ifndef environ
52: extern char **environ;
53: #endif
54:
55: /* Spawn a child in a fairly secure fashion. This returns the process
56: ID of the child or -1 on error. It takes far too many arguments:
57:
58: pazargs -- arguments (element 0 is command)
59: aidescs -- file descriptors for stdin, stdout and stderr
60: fkeepuid -- TRUE if euid should be left unchanged
61: fkeepenv -- TRUE if environment should be left unmodified
62: zchdir -- directory to chdir to
63: fnosigs -- TRUE if child should ignore SIGHUP, SIGINT and SIGQUIT
64: fshell -- TRUE if should try /bin/sh if execve gets ENOEXEC
65: zpath -- value for environment variable PATH
66: zuu_machine -- value for environment variable UU_MACHINE
67: zuu_user -- value for environment variable UU_USER
68:
69: The aidescs array is three elements long. 0 is stdin, 1 is stdout
70: and 2 is stderr. The array may contain either file descriptor
71: numbers to dup appropriately, or one of the following:
72:
73: SPAWN_NULL -- set descriptor to /dev/null
74: SPAWN_READ_PIPE -- set aidescs element to pipe for parent to read
75: SPAWN_WRITE_PIPE -- set aidescs element to pipe for parent to write
76:
77: If fkeepenv is FALSE, a standard environment is created. The
78: environment arguments (zpath, zuu_machine and zuu_user) are only
79: used if fkeepenv is FALSE; any of them may be NULL.
80:
81: This routine expects that all file descriptors have been set to
82: close-on-exec, so it doesn't have to worry about closing them
83: explicitly. It sets the close-on-exec flag for the new pipe
84: descriptors it returns. */
85:
86: pid_t
87: ixsspawn (pazargs, aidescs, fkeepuid, fkeepenv, zchdir, fnosigs, fshell,
88: zpath, zuu_machine, zuu_user)
89: const char **pazargs;
90: int aidescs[3];
91: boolean fkeepuid;
92: boolean fkeepenv;
93: const char *zchdir;
94: boolean fnosigs;
95: boolean fshell;
96: const char *zpath;
97: const char *zuu_machine;
98: const char *zuu_user;
99: {
100: char *zshcmd;
101: int i;
102: char *azenv[9];
103: char **pazenv;
104: boolean ferr;
105: int ierr = 0;
106: int onull;
107: int aichild_descs[3];
108: int cpar_close;
109: int aipar_close[4];
110: int cchild_close;
111: int aichild_close[3];
112: pid_t iret = 0;
113: const char *zcmd;
114:
115: /* If we might have to use the shell, allocate enough space for the
116: quoted command before forking. Otherwise the allocation would
117: modify the data segment and we could not safely use vfork. */
118: zshcmd = NULL;
119: if (fshell)
120: {
121: size_t clen;
122:
123: clen = 0;
124: for (i = 0; pazargs[i] != NULL; i++)
125: clen += strlen (pazargs[i]);
126: zshcmd = zbufalc (2 * clen + i);
127: }
128:
129: /* Set up a standard environment. This is again done before forking
130: because it will modify the data segment. */
131: if (fkeepenv)
132: pazenv = environ;
133: else
134: {
135: const char *zterm, *ztz;
136: char *zspace;
137: int ienv;
138:
139: if (zpath == NULL)
140: zpath = CMDPATH;
141:
142: azenv[0] = zbufalc (sizeof "PATH=" + strlen (zpath));
143: sprintf (azenv[0], "PATH=%s", zpath);
144: zspace = azenv[0] + sizeof "PATH=" - 1;
145: while ((zspace = strchr (zspace, ' ')) != NULL)
146: *zspace = ':';
147:
148: azenv[1] = zbufalc (sizeof "HOME=" + strlen (zSspooldir));
149: sprintf (azenv[1], "HOME=%s", zSspooldir);
150:
151: zterm = getenv ("TERM");
152: if (zterm == NULL)
153: zterm = "unknown";
154: azenv[2] = zbufalc (sizeof "TERM=" + strlen (zterm));
155: sprintf (azenv[2], "TERM=%s", zterm);
156:
157: azenv[3] = zbufcpy ("SHELL=/bin/sh");
158:
159: azenv[4] = zbufalc (sizeof "USER=" + strlen (OWNER));
160: sprintf (azenv[4], "USER=%s", OWNER);
161:
162: ienv = 5;
163:
164: ztz = getenv ("TZ");
165: if (ztz != NULL)
166: {
167: azenv[ienv] = zbufalc (sizeof "TZ=" + strlen (ztz));
168: sprintf (azenv[ienv], "TZ=%s", ztz);
169: ++ienv;
170: }
171:
172: if (zuu_machine != NULL)
173: {
174: azenv[ienv] = zbufalc (sizeof "UU_MACHINE="
175: + strlen (zuu_machine));
176: sprintf (azenv[ienv], "UU_MACHINE=%s", zuu_machine);
177: ++ienv;
178: }
179:
180: if (zuu_user != NULL)
181: {
182: azenv[ienv] = zbufalc (sizeof "UU_USER="
183: + strlen (zuu_user));
184: sprintf (azenv[ienv], "UU_USER=%s", zuu_user);
185: ++ienv;
186: }
187:
188: azenv[ienv] = NULL;
189: pazenv = azenv;
190: }
191:
192: /* Set up any needed pipes. */
193:
194: ferr = FALSE;
195: onull = -1;
196: cpar_close = 0;
197: cchild_close = 0;
198:
199: for (i = 0; i < 3; i++)
200: {
201: if (aidescs[i] == SPAWN_NULL)
202: {
203: if (onull < 0)
204: {
205: onull = open ((char *) "/dev/null", O_RDWR);
206: if (onull < 0
207: || fcntl (onull, F_SETFD,
208: fcntl (onull, F_GETFD, 0) | FD_CLOEXEC) < 0)
209: {
210: ierr = errno;
211: (void) close (onull);
212: ferr = TRUE;
213: break;
214: }
215: aipar_close[cpar_close] = onull;
216: ++cpar_close;
217: }
218: aichild_descs[i] = onull;
219: }
220: else if (aidescs[i] != SPAWN_READ_PIPE
221: && aidescs[i] != SPAWN_WRITE_PIPE)
222: aichild_descs[i] = aidescs[i];
223: else
224: {
225: int aipipe[2];
226:
227: if (pipe (aipipe) < 0)
228: {
229: ierr = errno;
230: ferr = TRUE;
231: break;
232: }
233:
234: if (aidescs[i] == SPAWN_READ_PIPE)
235: {
236: aidescs[i] = aipipe[0];
237: aichild_close[cchild_close] = aipipe[0];
238: aichild_descs[i] = aipipe[1];
239: aipar_close[cpar_close] = aipipe[1];
240: }
241: else
242: {
243: aidescs[i] = aipipe[1];
244: aichild_close[cchild_close] = aipipe[1];
245: aichild_descs[i] = aipipe[0];
246: aipar_close[cpar_close] = aipipe[0];
247: }
248:
249: ++cpar_close;
250: ++cchild_close;
251:
252: if (fcntl (aidescs[i], F_SETFD,
253: fcntl (aidescs[i], F_GETFD, 0) | FD_CLOEXEC) < 0)
254: {
255: ierr = errno;
256: ferr = TRUE;
257: break;
258: }
259: }
260: }
261:
262: #if DEBUG > 1
263: if (! ferr && FDEBUGGING (DEBUG_EXECUTE))
264: {
265: ulog (LOG_DEBUG_START, "Forking %s", pazargs[0]);
266: for (i = 1; pazargs[i] != NULL; i++)
267: ulog (LOG_DEBUG_CONTINUE, " %s", pazargs[i]);
268: ulog (LOG_DEBUG_END, "%s", "");
269: }
270: #endif
271:
272: if (! ferr)
273: {
274: /* This should really be vfork if available. */
275: iret = ixsfork ();
276: if (iret < 0)
277: {
278: ferr = TRUE;
279: ierr = errno;
280: }
281: }
282:
283: if (ferr)
284: {
285: for (i = 0; i < cchild_close; i++)
286: (void) close (aichild_close[i]);
287: iret = -1;
288: }
289:
290: if (iret != 0)
291: {
292: /* The parent. Close the child's ends of the pipes and return
293: the process ID, or an error. */
294: for (i = 0; i < cpar_close; i++)
295: (void) close (aipar_close[i]);
296: ubuffree (zshcmd);
297: if (! fkeepenv)
298: {
299: char **pz;
300:
301: for (pz = azenv; *pz != NULL; pz++)
302: ubuffree (*pz);
303: }
304: errno = ierr;
305: return iret;
306: }
307:
308: /* The child. */
309:
310: #ifdef STDIN_FILENO
311: #if STDIN_FILENO != 0 || STDOUT_FILENO != 1 || STDERR_FILENO != 2
312: #error The following code makes invalid assumptions
313: #endif
314: #endif
315:
316: for (i = 0; i < 3; i++)
317: {
318: if (aichild_descs[i] != i)
319: (void) dup2 (aichild_descs[i], i);
320: /* This should only be necessary if aichild_descs[i] == i, but
321: some systems copy the close-on-exec flag for a dupped
322: descriptor, which is wrong according to POSIX. */
323: (void) fcntl (i, F_SETFD, fcntl (i, F_GETFD, 0) &~ FD_CLOEXEC);
324: }
325:
326: zcmd = pazargs[0];
327: pazargs[0] = strrchr (zcmd, '/');
328: if (pazargs[0] == NULL)
329: pazargs[0] = zcmd;
330: else
331: ++pazargs[0];
332:
333: if (! fkeepuid)
334: {
335: (void) setuid (getuid ());
336: (void) setgid (getgid ());
337: }
338:
339: if (zchdir != NULL)
340: (void) chdir (zchdir);
341:
342: if (fnosigs)
343: {
344: #ifdef SIGHUP
345: (void) signal (SIGHUP, SIG_IGN);
346: #endif
347: #ifdef SIGINT
348: (void) signal (SIGINT, SIG_IGN);
349: #endif
350: #ifdef SIGQUIT
351: (void) signal (SIGQUIT, SIG_IGN);
352: #endif
353: }
354:
355: (void) execve ((char *) zcmd, (char **) pazargs, pazenv);
356:
357: /* The exec failed. If permitted, try using /bin/sh to execute a
358: shell script. */
359:
360: if (errno == ENOEXEC && fshell)
361: {
362: char *zto;
363: const char *azshargs[4];
364:
365: pazargs[0] = zcmd;
366: zto = zshcmd;
367: for (i = 0; pazargs[i] != NULL; i++)
368: {
369: const char *zfrom;
370:
371: for (zfrom = pazargs[i]; *zfrom != '\0'; zfrom++)
372: {
373: /* Some versions of /bin/sh appear to have a bug such
374: that quoting a '/' sometimes causes an error. I
375: don't know exactly when this happens (I can recreate
376: it on Ultrix 4.0), but in any case it is harmless to
377: not quote a '/'. */
378: if (*zfrom != '/')
379: *zto++ = '\\';
380: *zto++ = *zfrom;
381: }
382: *zto++ = ' ';
383: }
384: *(zto - 1) = '\0';
385:
386: azshargs[0] = "sh";
387: azshargs[1] = "-c";
388: azshargs[2] = zshcmd;
389: azshargs[3] = NULL;
390:
391: (void) execve ((char *) "/bin/sh", (char **) azshargs, pazenv);
392: }
393:
394: _exit (EXIT_FAILURE);
395:
396: /* Avoid compiler warning. */
397: return -1;
398: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.