Home | History | Annotate | Download | only in in.ftpd
      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