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