256 lines
5.9 KiB
C
256 lines
5.9 KiB
C
|
/* cgiexec.c by Michael Temari 02/17/96
|
||
|
*
|
||
|
* This file is part of httpd.
|
||
|
*
|
||
|
* 02/17/1996 Michael Temari <Michael@TemWare.Com>
|
||
|
* 07/07/1996 Initial Release Michael Temari <Michael@TemWare.Com>
|
||
|
* 12/29/2002 Michael Temari <Michael@TemWare.Com>
|
||
|
* 02/08/2005 Michael Temari <Michael@TemWare.Com>
|
||
|
*
|
||
|
*/
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <errno.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
#include "http.h"
|
||
|
#include "config.h"
|
||
|
#include "net.h"
|
||
|
|
||
|
_PROTOTYPE(char **cgienv, (struct http_request *rq, struct http_reply *rp));
|
||
|
_PROTOTYPE(static int addenv, (char *name, char *value, char **buf, int *len));
|
||
|
|
||
|
int cgiexec(rq, rp)
|
||
|
struct http_request *rq;
|
||
|
struct http_reply *rp;
|
||
|
{
|
||
|
struct stat st;
|
||
|
char *prog;
|
||
|
int cmdpid;
|
||
|
int status;
|
||
|
char *argv[5];
|
||
|
int ifds[2];
|
||
|
int ofds[2];
|
||
|
static char cmd[2048];
|
||
|
char **cmdenv;
|
||
|
int dirflag = 0;
|
||
|
|
||
|
if(stat(rp->realurl, &st)) {
|
||
|
if(errno == EACCES)
|
||
|
rp->status = HTTP_STATUS_FORBIDDEN;
|
||
|
else
|
||
|
rp->status = HTTP_STATUS_NOT_FOUND;
|
||
|
strcpy(rp->statusmsg, strerror(errno));
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
if((st.st_mode & S_IFMT) == S_IFDIR)
|
||
|
if(direxec != NULL) {
|
||
|
prog = direxec; dirflag = 1;
|
||
|
} else
|
||
|
return(0);
|
||
|
else
|
||
|
prog = rp->realurl;
|
||
|
|
||
|
/* check if prog is allowed to be exec'd */
|
||
|
if(!dirflag && !(rp->urlaccess & URLA_EXEC))
|
||
|
return(0);
|
||
|
|
||
|
/* if cannot exec mode then return */
|
||
|
if( (st.st_mode & S_IXUSR) == 0 &&
|
||
|
(st.st_mode & S_IXGRP) == 0 &&
|
||
|
(st.st_mode & S_IXOTH) == 0 )
|
||
|
return(0);
|
||
|
|
||
|
if((cmdenv = cgienv(rq, rp)) == NULL) {
|
||
|
rp->status = HTTP_STATUS_SERVER_ERROR;
|
||
|
strcpy(rp->statusmsg, "Could not setup cgi environment");
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
argv[0] = prog;
|
||
|
argv[1] = rp->realurl;
|
||
|
argv[2] = rq->url;
|
||
|
argv[3] = (char *)NULL;
|
||
|
|
||
|
if(pipe(ifds) < 0) {
|
||
|
rp->status = HTTP_STATUS_NOT_FOUND;
|
||
|
strcpy(rp->statusmsg, strerror(errno));
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
if(pipe(ofds) < 0) {
|
||
|
rp->status = HTTP_STATUS_NOT_FOUND;
|
||
|
strcpy(rp->statusmsg, strerror(errno));
|
||
|
close(ifds[0]); close(ifds[1]);
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
if((cmdpid = fork()) < 0) {
|
||
|
close(ifds[0]); close(ofds[0]);
|
||
|
close(ifds[1]); close(ofds[1]);
|
||
|
rp->status = HTTP_STATUS_NOT_FOUND;
|
||
|
strcpy(rp->statusmsg, strerror(errno));
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
/* We don't know how much data is going to be passed back */
|
||
|
rp->size = 0;
|
||
|
|
||
|
if(cmdpid == 0) { /* Child */
|
||
|
#if 0
|
||
|
if((cmdpid = fork()) < 0) {
|
||
|
close(ifds[0]); close(ofds[0]);
|
||
|
close(ifds[1]); close(ofds[1]);
|
||
|
exit(-1);
|
||
|
}
|
||
|
if(cmdpid != 0) {
|
||
|
close(ifds[0]); close(ofds[0]);
|
||
|
close(ifds[1]); close(ofds[1]);
|
||
|
exit(0);
|
||
|
}
|
||
|
#endif
|
||
|
setsid();
|
||
|
close(ifds[0]); close(ofds[1]);
|
||
|
dup2(ofds[0], 0);
|
||
|
dup2(ifds[1], 1);
|
||
|
dup2(ifds[1], 2);
|
||
|
close(ifds[1]); close(ofds[0]);
|
||
|
execve(argv[0], argv, cmdenv);
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
/* Get rid of Zombie child */
|
||
|
(void) wait(&status);
|
||
|
#endif
|
||
|
|
||
|
close(ifds[1]); close(ofds[0]);
|
||
|
|
||
|
rp->fd = ifds[0];
|
||
|
rp->ofd = ofds[1];
|
||
|
rp->pid = cmdpid;
|
||
|
|
||
|
if(rp->urlaccess & URLA_HEADERS)
|
||
|
rp->headers = -1;
|
||
|
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
char **cgienv(rq, rp)
|
||
|
struct http_request *rq;
|
||
|
struct http_reply *rp;
|
||
|
{
|
||
|
static char buffer[4096];
|
||
|
char *p, *p2;
|
||
|
char **e;
|
||
|
int len;
|
||
|
char temp[20];
|
||
|
|
||
|
p = buffer;
|
||
|
len = sizeof(buffer);
|
||
|
|
||
|
if(addenv("PATH", "/usr/local/bin:/bin:/usr/bin", &p, &len)) return(NULL);
|
||
|
if(getenv("TZ") != (char *)NULL)
|
||
|
if(addenv("TZ", getenv("TZ"), &p, &len)) return(NULL);
|
||
|
|
||
|
/* HACK - some of these are hardcoded and should not be MAT 3/17/96 */
|
||
|
|
||
|
/* HTTP_ */
|
||
|
|
||
|
if(addenv("SERVER_SOFTWARE", "Temari httpd/1.0", &p, &len)) return(NULL);
|
||
|
if(addenv("SERVER_NAME", myhostname, &p, &len)) return(NULL);
|
||
|
if(addenv("GATEWAY_INTERFACE", "CGI/1.1", &p, &len)) return(NULL);
|
||
|
if(addenv("SERVER_PROTOCOL", "HTTP/1.0", &p, &len)) return(NULL);
|
||
|
if(rq->port)
|
||
|
sprintf(temp, "%u", rq->port);
|
||
|
else
|
||
|
strcpy(temp, "80");
|
||
|
if(addenv("SERVER_PORT", temp, &p, &len)) return(NULL);
|
||
|
switch(rq->method) {
|
||
|
case HTTP_METHOD_GET:
|
||
|
if(addenv("REQUEST_METHOD", "GET", &p, &len)) return(NULL);
|
||
|
break;
|
||
|
case HTTP_METHOD_POST:
|
||
|
if(addenv("REQUEST_METHOD", "POST", &p, &len)) return(NULL);
|
||
|
break;
|
||
|
case HTTP_METHOD_HEAD:
|
||
|
if(addenv("REQUEST_METHOD", "HEAD", &p, &len)) return(NULL);
|
||
|
break;
|
||
|
case HTTP_METHOD_PUT:
|
||
|
if(addenv("REQUEST_METHOD", "PUT", &p, &len)) return(NULL);
|
||
|
break;
|
||
|
default:
|
||
|
if(addenv("REQUEST_METHOD", "UNKNOWN", &p, &len)) return(NULL);
|
||
|
}
|
||
|
if(addenv("PATH_INFO", "?", &p, &len)) return(NULL);
|
||
|
if(addenv("PATH_TRANSLATED", "?", &p, &len)) return(NULL);
|
||
|
if(addenv("SCRIPT_NAME", rq->url, &p, &len)) return(NULL);
|
||
|
if(addenv("QUERY_STRING", rq->query, &p, &len)) return(NULL);
|
||
|
if(addenv("REMOTE_HOST", rmthostname, &p, &len)) return(NULL);
|
||
|
if(addenv("REMOTE_ADDR", rmthostaddr, &p, &len)) return(NULL);
|
||
|
if(rq->authuser != (char *)NULL)
|
||
|
if(addenv("AUTH_USER", rq->authuser, &p, &len)) return(NULL);
|
||
|
/* AUTH_TYPE */
|
||
|
/* REMOTE_USER */
|
||
|
/* REMOTE_IDENT */
|
||
|
if(rq->method == HTTP_METHOD_POST) {
|
||
|
if(addenv("CONTENT_TYPE", "application/x-www-form-urlencoded", &p, &len)) return(NULL);
|
||
|
sprintf(temp, "%lu", rq->size);
|
||
|
if(addenv("CONTENT_LENGTH", temp, &p, &len)) return(NULL);
|
||
|
}
|
||
|
/* COOKIE */
|
||
|
if(rq->cookie[0] != '\0')
|
||
|
if(addenv("COOKIE", rq->cookie, &p, &len)) return(NULL);
|
||
|
/* HOST */
|
||
|
if(addenv("HOST", rq->host, &p, &len)) return(NULL);
|
||
|
|
||
|
if(len < 1) return(NULL);
|
||
|
*p++ = '\0';
|
||
|
|
||
|
p2 = buffer;
|
||
|
e = (char **)p;
|
||
|
while(*p2) {
|
||
|
if(len < sizeof(e)) return(NULL);
|
||
|
len -= sizeof(e);
|
||
|
*e++ = p2;
|
||
|
while(*p2) p2++;
|
||
|
p2++;
|
||
|
}
|
||
|
if(len < sizeof(e)) return(NULL);
|
||
|
*e++ = NULL;
|
||
|
|
||
|
return((char **)p);
|
||
|
}
|
||
|
|
||
|
static int addenv(name, value, buf, len)
|
||
|
char *name;
|
||
|
char *value;
|
||
|
char **buf;
|
||
|
int *len;
|
||
|
{
|
||
|
char *p;
|
||
|
int size;
|
||
|
|
||
|
p = *buf;
|
||
|
|
||
|
size = strlen(name)+1+strlen(value)+1;
|
||
|
|
||
|
if(size > *len)
|
||
|
return(-1);
|
||
|
|
||
|
sprintf(p, "%s=%s", name, value);
|
||
|
|
||
|
p += size;
|
||
|
*buf = p;
|
||
|
*len -= size;
|
||
|
|
||
|
return(0);
|
||
|
}
|