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);
 | |
| }
 | 
