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