/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1988, 1993, 19944* The Regents of the University of California. All rights reserved.5*6* This code is derived from software written by Ken Arnold and7* published in UNIX Review, Vol. 6, No. 8.8*9* Redistribution and use in source and binary forms, with or without10* modification, are permitted provided that the following conditions11* are met:12* 1. Redistributions of source code must retain the above copyright13* notice, this list of conditions and the following disclaimer.14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17* 3. Neither the name of the University nor the names of its contributors18* may be used to endorse or promote products derived from this software19* without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND22* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE23* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE24* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE25* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL26* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS27* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)28* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT29* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY30* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF31* SUCH DAMAGE.32*/3334#include <sys/types.h>35#include <sys/wait.h>36#include <netinet/in.h>3738#include <errno.h>39#include <glob.h>40#include <signal.h>41#include <stdio.h>42#include <stdlib.h>43#include <string.h>44#include <unistd.h>4546#include "extern.h"47#include "pathnames.h"48#include <syslog.h>49#include <time.h>5051#define MAXUSRARGS 10052#define MAXGLOBARGS 10005354/*55* Special version of popen which avoids call to shell. This ensures no one56* may create a pipe to a hidden program as a side effect of a list or dir57* command.58*/59static int *pids;60static int fds;6162FILE *63ftpd_popen(char *program, char *type)64{65char *cp;66FILE *iop;67int argc, gargc, pdes[2], pid;68char **pop, *argv[MAXUSRARGS], *gargv[MAXGLOBARGS];6970if (((*type != 'r') && (*type != 'w')) || type[1])71return (NULL);7273if (!pids) {74if ((fds = getdtablesize()) <= 0)75return (NULL);76if ((pids = calloc(fds, sizeof(int))) == NULL)77return (NULL);78}79if (pipe(pdes) < 0)80return (NULL);8182/* break up string into pieces */83for (argc = 0, cp = program; argc < MAXUSRARGS; cp = NULL) {84if (!(argv[argc++] = strtok(cp, " \t\n")))85break;86}87argv[argc - 1] = NULL;8889/* glob each piece */90gargv[0] = argv[0];91for (gargc = argc = 1; argv[argc] && gargc < (MAXGLOBARGS-1); argc++) {92glob_t gl;93int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;9495memset(&gl, 0, sizeof(gl));96gl.gl_matchc = MAXGLOBARGS;97flags |= GLOB_LIMIT;98if (glob(argv[argc], flags, NULL, &gl))99gargv[gargc++] = strdup(argv[argc]);100else if (gl.gl_pathc > 0) {101for (pop = gl.gl_pathv; *pop && gargc < (MAXGLOBARGS-1);102pop++)103gargv[gargc++] = strdup(*pop);104}105globfree(&gl);106}107gargv[gargc] = NULL;108109iop = NULL;110fflush(NULL);111pid = (strcmp(gargv[0], _PATH_LS) == 0) ? fork() : vfork();112switch(pid) {113case -1: /* error */114(void)close(pdes[0]);115(void)close(pdes[1]);116goto pfree;117/* NOTREACHED */118case 0: /* child */119if (*type == 'r') {120if (pdes[1] != STDOUT_FILENO) {121dup2(pdes[1], STDOUT_FILENO);122(void)close(pdes[1]);123}124dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */125(void)close(pdes[0]);126} else {127if (pdes[0] != STDIN_FILENO) {128dup2(pdes[0], STDIN_FILENO);129(void)close(pdes[0]);130}131(void)close(pdes[1]);132}133/* Drop privileges before proceeding */134if (getuid() != geteuid() && setuid(geteuid()) < 0)135_exit(1);136if (strcmp(gargv[0], _PATH_LS) == 0) {137/* Reset getopt for ls_main() */138optreset = optind = optopt = 1;139/* Close syslogging to remove pwd.db missing msgs */140closelog();141/* Trigger to sense new /etc/localtime after chroot */142if (getenv("TZ") == NULL) {143setenv("TZ", "", 0);144tzset();145unsetenv("TZ");146tzset();147}148exit(ls_main(gargc, gargv));149}150execv(gargv[0], gargv);151_exit(1);152}153/* parent; assume fdopen can't fail... */154if (*type == 'r') {155iop = fdopen(pdes[0], type);156(void)close(pdes[1]);157} else {158iop = fdopen(pdes[1], type);159(void)close(pdes[0]);160}161pids[fileno(iop)] = pid;162163pfree: for (argc = 1; gargv[argc] != NULL; argc++)164free(gargv[argc]);165166return (iop);167}168169int170ftpd_pclose(FILE *iop)171{172int fdes, omask, status;173pid_t pid;174175/*176* pclose returns -1 if stream is not associated with a177* `popened' command, or, if already `pclosed'.178*/179if (pids == NULL || pids[fdes = fileno(iop)] == 0)180return (-1);181(void)fclose(iop);182omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));183while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR)184continue;185(void)sigsetmask(omask);186pids[fdes] = 0;187if (pid < 0)188return (pid);189if (WIFEXITED(status))190return (WEXITSTATUS(status));191return (1);192}193194195