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