pipe.c:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/wait.h>
// pointer to array allocated at run-time
static pid_t *childpid = NULL;
// From our open_max()
static int maxfd;
FILE *popen(const char *cmdstring, const char *type)
{
int i;
int pfd[2];
pid_t pid;
FILE *fp;
// only allow "r" or "w"
if((type[0] != 'r' && type[0] != 'w') || type[1] != 0) {
errno = EINVAL;
return NULL;
}
// first time to allocate childpid
if(childpid == NULL) {
maxfd = 4096;
if((childpid = calloc(maxfd, sizeof(pid_t))) == NULL) {
return NULL;
}
}
// Setup the pipe
if(pipe(pfd) < 0) return NULL;
// Create the child process
if((pid = fork()) < 0) {
return NULL;
} else if(pid == 0) { // child process
// Handle the pipe file descriptor
if(*type == 'r') {
close(pfd[0]);
if(pfd[1] != STDOUT_FILENO) {
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
}
} else {
close(pfd[1]);
if(pfd[0] != STDIN_FILENO) {
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
}
}
// close all existing descriptors which is opened before
// this is required by POSIX.1 standard
for(i = 0; i < maxfd; i++)
if(childpid[i] > 0)
close(i);
// Execute the descriptors
execl("/bin/sh", "sh", "-c", cmdstring, (char*)0);
_exit(127);
}
// parent process open the file stream from pipe descriptor
if(*type == 'r') {
close(pfd[1]);
if((fp = fdopen(pfd[0], type)) == NULL) return NULL;
} else {
close(pfd[0]);
if((fp = fdopen(pfd[1], type)) == NULL) return NULL;
}
childpid[fileno(fp)] = pid;
return fp;
}
int pclose(FILE* fp)
{
int fd, stat;
pid_t pid;
// popen has never been called.
if(childpid == NULL) {
errno = EINVAL;
return -1;
}
fd = fileno(fp);
// fp wasn't opened by popen()
if((pid = childpid[fd]) == 0) {
errno = EINVAL;
return -1;
}
childpid[fd] = 0;
if(fclose(fp) == EOF)
return -1;
while(waitpid(pid, &stat, 0) < 0) {
if(errno != EINTR)
return -1;
}
return stat;
}
int main(int argc, char* argv[])
{
exit(0);
}
Note: popen should never be called by a set-user-id or set-group-id program. when it executes the command with the environment inherited by the caller.
A malicious user can manipulate the environment so that the shell executes commands other than those intended, with the elevated permissions granted by the set-ID file mode.
If the parent process setup the signal handler for SIGCHLD, waitpid is interrupted when child process exits, at this time, errno == EINTR, then we simply retry waitpid again (while waitpid loop).
No comments:
Post a Comment