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 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 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 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 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 */