xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/popen.c (revision 12333:f835be4545bb)
1 /*
2  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /****************************************************************************
6   Copyright (c) 1999,2000 WU-FTPD Development Group.
7   All rights reserved.
8 
9   Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
10     The Regents of the University of California.
11   Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
12   Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
13   Portions Copyright (c) 1989 Massachusetts Institute of Technology.
14   Portions Copyright (c) 1998 Sendmail, Inc.
15   Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P.  Allman.
16   Portions Copyright (c) 1997 by Stan Barber.
17   Portions Copyright (c) 1997 by Kent Landfield.
18   Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
19     Free Software Foundation, Inc.
20 
21   Use and distribution of this software and its source code are governed
22   by the terms and conditions of the WU-FTPD Software License ("LICENSE").
23 
24   If you did not receive a copy of the license, it may be obtained online
25   at http://www.wu-ftpd.org/license.html.
26 
27   $Id: popen.c,v 1.16 2000/07/01 18:17:39 wuftpd Exp $
28 
29 ****************************************************************************/
30 #include "config.h"
31 
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <limits.h>
38 #if defined(HAVE_FCNTL_H)
39 #include <fcntl.h>
40 #endif
41 #include "pathnames.h"
42 #include "proto.h"
43 
44 /*
45  * Special version of popen which avoids call to shell.  This insures noone
46  * may create a pipe to a hidden program as a side effect of a list or dir
47  * command.
48  */
49 static int popen_fd = -1;
50 static pid_t popen_pid = -1;
51 /*
52  * The globbed argv could end up being huge, so we must dynamically allocate
53  * it.  Allocate it in chunks of GARGV_INC pointers.
54  */
55 #define GARGV_INC	100
56 #define ARGV_INC	5
57 
58 static char **argv;
59 static char **gargv;
60 static int argv_size;
61 static int gargv_size;
62 
ftpd_popen(char * program,char * type,int closestderr)63 FILE *ftpd_popen(char *program, char *type, int closestderr)
64 {
65     register char *cp;
66     FILE *iop = NULL;
67     int argc, gargc, pdes[2], i, devnullfd;
68     char **pop, *vv[2];
69     extern char *globerr;
70 
71     /*
72      * ftpd never needs more than one pipe open at a time, so only one PID is
73      * stored (in popen_pid). Protect against multiple pipes in case this
74      * changes.
75      */
76     if (popen_fd != -1)
77 	return (NULL);
78 
79     if ((*type != 'r' && *type != 'w') || type[1])
80 	return (NULL);
81 
82     if (gargv == NULL) {
83 	gargv = (char **)malloc(GARGV_INC * sizeof (char *));
84 	if (gargv == NULL) {
85 	    return (NULL);
86 	}
87 	gargv_size = GARGV_INC;
88     }
89 
90     if (argv == NULL) {
91 	argv = (char **)malloc(ARGV_INC * sizeof (char *));
92 	if (argv == NULL) {
93 	    return (NULL);
94 	}
95 	argv_size = ARGV_INC;
96     }
97 
98     if (pipe(pdes) < 0)
99 	return (NULL);
100 
101     /* empty the array */
102     (void) memset((void *) argv, 0, argv_size * sizeof(char *));
103     /* break up string into pieces */
104     for (argc = 0, cp = program; ;cp = NULL) {
105 	if (!(argv[argc++] = strtok(cp, " \t\n"))) {
106 	    break;
107 	}
108 	if (argc >= argv_size) {
109 	    char **tmp;
110 
111 	    tmp = (char **)realloc(argv,
112 		(argv_size + ARGV_INC) * sizeof (char *));
113 	    if (tmp == NULL) {
114 		(void) close(pdes[0]);
115 		(void) close(pdes[1]);
116 		return (NULL);
117 	    } else {
118 		argv = tmp;
119 		argv_size += ARGV_INC;
120 	    }
121 	}
122     }
123 
124     /* glob each piece */
125     gargv[0] = argv[0];
126     for (gargc = argc = 1; argv[argc]; argc++) {
127 	if (!(pop = ftpglob(argv[argc], B_TRUE)) || globerr != NULL) {	/* globbing failed */
128 	    if (pop) {
129 		blkfree(pop);
130 		free((char *) pop);
131 	    }
132 	    vv[0] = strspl(argv[argc], "");
133 	    vv[1] = NULL;
134 	    pop = copyblk(vv);
135 	}
136 	argv[argc] = (char *) pop;	/* save to free later */
137 	while (*pop) {
138 	    gargv[gargc++] = *pop++;
139 	    if (gargc >= gargv_size) {
140 		char **tmp;
141 
142 		tmp = (char **)realloc(gargv,
143 		    (gargv_size + GARGV_INC) * sizeof (char *));
144 		if (tmp == NULL) {
145 		    (void) close(pdes[0]);
146 		    (void) close(pdes[1]);
147 		    goto pfree;
148 		} else {
149 		    gargv = tmp;
150 		    gargv_size += GARGV_INC;
151 		}
152 	    }
153 	}
154     }
155     gargv[gargc] = NULL;
156 
157 #ifdef SIGCHLD
158     (void) signal(SIGCHLD, SIG_DFL);
159 #endif
160     switch (popen_pid = vfork()) {
161     case -1:			/* error */
162 	(void) close(pdes[0]);
163 	(void) close(pdes[1]);
164 	goto pfree;
165 	/* NOTREACHED */
166     case 0:			/* child */
167 	if (*type == 'r') {
168 	    if (pdes[1] != 1) {
169 		dup2(pdes[1], 1);
170 		if (closestderr) {
171 		    (void) close(2);
172 		    /* stderr output is written to fd 2, so make sure it isn't
173 		     * available to be assigned to another file */
174 		    if ((devnullfd = open(_PATH_DEVNULL, O_RDWR)) != -1) {
175 			if (devnullfd != 2) {
176 			    dup2(devnullfd, 2);
177 			    (void) close(devnullfd);
178 			}
179 		    }
180 		}
181 		else
182 		    dup2(pdes[1], 2);	/* stderr, too! */
183 		(void) close(pdes[1]);
184 	    }
185 	    (void) close(pdes[0]);
186 	}
187 	else {
188 	    if (pdes[0] != 0) {
189 		dup2(pdes[0], 0);
190 		(void) close(pdes[0]);
191 	    }
192 	    (void) close(pdes[1]);
193 	}
194 	closefds(3);
195 	/* begin CERT suggested fixes */
196 	close(0);
197 	i = geteuid();
198 	setid_priv_on(0);
199 	setgid(getegid());
200 	setuid(i);
201 	setid_priv_off(i);
202 	/* end CERT suggested fixes */
203 	execv(gargv[0], gargv);
204 	perror(gargv[0]);
205 	_exit(1);
206     }
207     /* parent; assume fdopen can't fail...  */
208     if (*type == 'r') {
209 	iop = fdopen(pdes[0], type);
210 	(void) close(pdes[1]);
211     }
212     else {
213 	iop = fdopen(pdes[1], type);
214 	(void) close(pdes[0]);
215     }
216     popen_fd = fileno(iop);
217 
218   pfree:for (argc = 1; argv[argc]; argc++) {
219 	blkfree((char **) argv[argc]);
220 	free((char *) argv[argc]);
221     }
222     return (iop);
223 }
224 
ftpd_pclose(FILE * iop)225 int ftpd_pclose(FILE *iop)
226 {
227     pid_t pid;
228 #if defined(HAVE_SIGPROCMASK) || (defined(SVR4) && !defined(AUTOCONF))
229     sigset_t sig, omask;
230     int stat_loc;
231     sigemptyset(&sig);
232     sigaddset(&sig, SIGINT);
233     sigaddset(&sig, SIGQUIT);
234     sigaddset(&sig, SIGHUP);
235 #elif defined (_OSF_SOURCE)
236     int omask;
237     int status;
238 #else
239     int omask;
240     union wait stat_loc;
241 #endif
242 
243     /* pclose returns -1 if stream is not associated with a `popened'
244      * command, or, if already `pclosed'. */
245     if ((popen_fd == -1) || (popen_fd != fileno(iop)))
246 	return (-1);
247     (void) fclose(iop);
248 #if defined(HAVE_SIGPROCMASK) || (!defined(AUTOCONF) && defined(SVR4))
249     sigprocmask(SIG_BLOCK, &sig, &omask);
250 #else
251     omask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGHUP));
252 #endif
253 
254 #if (!defined(HAVE_SIGPROCMASK) || (!defined(SVR4) && !defined(AUTOCONF))) && defined (_OSF_SOURCE)
255     while ((pid = wait(&status)) != popen_pid && pid != -1);
256 #elif ! defined(NeXT)
257     while ((pid = wait((int *) &stat_loc)) != popen_pid && pid != -1);
258 #else
259     while ((pid = wait(&stat_loc)) != popen_pid && pid != -1);
260 #endif
261     popen_pid = -1;
262     popen_fd = -1;
263 #ifdef SIGCHLD
264     (void) signal(SIGCHLD, SIG_IGN);
265 #endif
266 #if defined(HAVE_SIGPROCMASK) || (defined(SVR4) && !defined(AUTOCONF))
267     sigprocmask(SIG_SETMASK, &omask, (sigset_t *) NULL);
268     return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
269 #else
270     (void) sigsetmask(omask);
271 #ifdef _OSF_SOURCE
272     return (pid == -1 ? -1 : status);
273 #elif defined(LINUX)
274     return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
275 #else
276     return (pid == -1 ? -1 : stat_loc.w_status);
277 #endif
278 #endif
279 }
280 
281 #ifdef CLOSEFROM
closefds(int startfd)282 void closefds(int startfd)
283 {
284     closefrom(startfd);
285 }
286 #else
287 
288 #ifdef HAVE_GETRLIMIT
289 #include <sys/resource.h>
290 #endif
291 
closefds(int startfd)292 void closefds(int startfd)
293 {
294     int i, fds;
295 #ifdef HAVE_GETRLIMIT
296     struct rlimit rlp;
297 #endif
298 
299 #ifdef OPEN_MAX
300     fds = OPEN_MAX;
301 #else
302     fds = 31;
303 #endif
304 
305 #ifdef HAVE_GETRLIMIT
306     if ((getrlimit(RLIMIT_NOFILE, &rlp) == 0) &&
307 	(rlp.rlim_cur != RLIM_INFINITY)) {
308 	fds = rlp.rlim_cur;
309     }
310 #else
311 #ifdef HAVE_GETDTABLESIZE
312     if ((i = getdtablesize()) > 0)
313 	fds = i;
314 #else
315 #ifdef HAVE_SYSCONF
316     fds = sysconf(_SC_OPEN_MAX);
317 #endif /* HAVE_SYSCONF */
318 #endif /* HAVE_GETDTABLESIZE */
319 #endif /* HAVE_GETRLIMIT */
320 
321     for (i = startfd; i < fds; i++)
322 	close(i);
323 }
324 #endif /* CLOSEFROM */
325