Secure version of popen() needed
Eric S. Raymond
eric at snark.UUCP
Tue May 10 00:37:03 AEST 1988
OK, I give up. Can someone *else* spot the bug in this code? The idea is to
craft a secure replacement for popen() that does its own parsing of I/O
redirections on the command line, then forks a bare process rather than a
shell. The problem arises when it actually tries to do the I/O redirection.
Please see the comment under 'PROBLEM SECTION STARTS HERE'.
Please reply by email, a summary of results will be posted.
------------------------------------------------------------------------------
#include <stdio.h>
#ifndef _NFILE /* this will be defined correctly on USG systems */
#define _NFILE 64
#endif
#define MAXARGS 64 /* max # of arguments accepted for secure command */
#define RDR 0
#define WTR 1
static int peopen_pid[_NFILE];
FILE *peopen(cmd, mode)
/*
* This is a modified version of popen, made more secure. Rather than
* forking off a shell, you get a bare process.
*/
char *cmd, *mode;
{
int pipes[2];
register int *poptr;
register int myside, yourside, pid, wmode = (strcmp(mode, "w") == 0);
char *cp, line[BUFSIZ], *largv[MAXARGS], *redirect = (char *)NULL;
int largc;
/* crack the argument list into a dope vector */
(void) strcpy(line, cmd);
for (cp = line; *cp; cp++)
{
if (isspace(*cp))
*cp = '\0';
else if (cp == line || cp[-1] == 0)
{
/* I/O redirection tokens aren't arguments */
if (*cp == '<' || *cp == '>')
{
if ((wmode && *cp == '>') || (!wmode && *cp == '<'))
redirect = cp + 1;
else
return((FILE *)NULL);
}
else if (largc >= MAXARGS - 1)
return((FILE *)NULL);
else
largv[largc++] = cp;
}
}
largv[largc] = (char *) NULL;
if (pipe(pipes) < 0)
return((FILE *)NULL);
myside = (wmode ? pipes[WTR] : pipes[RDR]);
yourside = (wmode ? pipes[RDR] : pipes[WTR]);
if ((pid = fork()) == 0) /* myside/yourside reverse roles in child */
{
/* close all pipes from other popen's */
for (poptr = peopen_pid; poptr < peopen_pid+20; poptr++) {
if(*poptr)
(void) close(poptr - peopen_pid);
}
(void) close(myside);
(void) close(wmode ? 0 : 1);
(void) dup(yourside);
(void) close(yourside);
/* do redirections */
if (redirect != (char *)NULL)
{
/*
* PROBLEM SECTION BEGINS HERE
*
* In theory, what we're doing is a) opening the file we want to redirect to,
* b) closing the fd corresponding to either stdin or stdout (depending on
* mode), c) dup()ing the redirect file descriptor into the slot we just
* closed, and d) closing the original redirect fd. Instrumenting the code
* confirms that this is in fact happening as we'd expect
*
* The net result should be that the redirect file is attached to stdin
* (if mode was "r") or stdout (if mode was "w") of the child process,
* which is what we want.
*
* What's actually happening is that the calling process is dying with
* signal 13 (broken pipe). Help?
*
*/
FILE *fp;
int fd;
fp = fopen(redirect, mode);
if (fp == (FILE *)NULL || (fd = fileno(fp)) == FAIL)
_exit(2);
(void) close(wmode);
(void) dup(fd);
(void) close(fd);
}
(void) execv(largv[0], largv);
_exit(1);
}
if(pid == -1)
return((FILE *)NULL);
peopen_pid[myside] = pid;
(void) close(yourside);
return(fdopen(myside, mode));
}
/* peopen.c ends here */
------------------------------------------------------------------------------
--
Eric S. Raymond (the mad mastermind of TMN-Netnews)
UUCP: {{uunet,rutgers,ihnp4}!cbmvax,rutgers!vu-vlsi,att}!snark!eric
Post: 22 South Warren Avenue, Malvern, PA 19355 Phone: (215)-296-5718
More information about the Comp.unix.wizards
mailing list