diff -u -r -N webfs-orig/Makefile webfs/Makefile --- webfs-orig/Makefile Wed May 2 11:35:26 2001 +++ webfs/Makefile Tue Sep 25 23:58:11 2001 @@ -13,7 +13,7 @@ CFLAGS += -DMIMEFILE=\"$(mimefile)\" PROG=webfsd -OBJS=main.o request.o response.o ls.o mime.o +OBJS=main.o request.o response.o ls.o mime.o cgi.o all build: $(PROG) diff -u -r -N webfs-orig/README.userdir webfs/README.userdir --- webfs-orig/README.userdir Thu Jan 1 01:00:00 1970 +++ webfs/README.userdir Mon Oct 1 21:26:57 2001 @@ -0,0 +1,8 @@ +I implemented user directories (e.g. /home/chris/public_html). +It does only work, if you do not chroot() webfs + +The patch should be available at http://c0re.jp + +This software was brought yo you by the +.:. Teenage Mutant Hero c0re Coders .:. + http://c0re.jp/ diff -u -r -N webfs-orig/cgi.c webfs/cgi.c --- webfs-orig/cgi.c Thu Jan 1 01:00:00 1970 +++ webfs/cgi.c Mon Oct 1 19:23:33 2001 @@ -0,0 +1,226 @@ +/* cgi.c - CGI support for webfs + * + * (c)2001 Christian Klein + * + * This software was brought to you by + * .:. Teenage Mutant Hero c0re Coders .:. + * http://c0re.jp + * + * credits to azzit + * and drt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "httpd.h" + +extern char *cgi_dir; +extern char *server_name; +extern char *listen_port; + +int cgi(struct REQUEST *req) +{ + int c2p[2], p2c[2]; + pid_t child_pid; + int i; + char buffer[BUFSIZ]; + time_t now; + char **env=NULL; + + /* open two pipes for bidirectional communication */ + if ((pipe(c2p))) + { + perror("Unable to open child_stdout->parent_stdin pipe"); + return 0; + } + + if ((pipe(p2c))) + { + perror("Unable to open child_stdout->parent_stdin pipe"); + return 0; + } + + if ((child_pid = fork())) + { + char *body=NULL; + int pos=0; + + close(c2p[1]); + close(p2c[0]); + + /* TODO: input for cgi, GET: via environment, POST: via stdin + * + * do i have to use select for stdin/-out handling? + * + */ + + memset(buffer, 0, BUFSIZ); + while ((i = read(c2p[0], buffer, BUFSIZ))) + { + if (i<1) + { + perror(NULL); + exit(0); + } else { + body = (char *) realloc (body, BUFSIZ); + /* TODO: realloc check */ + memcpy(body+pos, buffer, BUFSIZ); + pos += i; + memset(buffer, 0, 4096); + } + } + + req->body = body; + req->lbody = pos; + /* TODO: set content type */ + /* better: get content type from script */ + req->mime = "text/html"; + now=time(NULL); + mkheader(req,200,now); + free(body); + + return 0; + } else { + /* child */ + unsigned char c, last=0; + char *s, *query, *cutted, path[4096]; + /* the whitelist is created with contrib/whitelist.pl */ + char whitelist[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + close(c2p[0]); + close(p2c[1]); + close(0); + close(1); + dup2(c2p[1], 1); + dup2(p2c[0], 0); + + /* cutted path is path without /cgi-bin/ */ + cutted = req->path; + cutted = strchr(cutted+1, '/') + 1; + query = strchr(cutted, '?'); + *query++ = 0; + + /* check for unwanted charactes */ + s = cutted; + while ((c=*s++)) + { + if (whitelist[c] == 0) + { + mkerror(req, 404, 1); + return -1; + } + + if (c=='.' && last=='.') + { + mkerror(req, 404, 1); + return -1; + } + + last = c; + } + + /* create the pathname */ + memset(path, 0, 4096); + strcat(path, cgi_dir); + if (path[strlen(path)-1] != '/') + path[strlen(path)] = '/'; + strncat(path, cutted, 4096-strlen(cgi_dir)); + + /* build the new environment */ + env = build_env(req, cutted, path, query); + + fprintf(stderr, "exec: %s\n", path); + if ((execle(path, req->path, NULL, env))) + { + /* TODO: would a 403 be better? */ + mkerror(req, 404, 1); + destroy_environment(env); + return -1; + } + } + + return 0; +} + +char **build_env(struct REQUEST *req, char *filename, char *path, char *query) +{ + size_t size=8192; + int i=0; + char **env; + + /* do I have to alloc memory for the char ** ? */ + env = (char **) malloc (size); + if (!env) + return NULL; + + memset(env, 0, size); + + if ((env[i] = concat("SERVER_SOFTWARE", server_name))) + i++; + if ((env[i] = concat("SERVER_NAME", req->hostname))) + i++; + if ((env[i] = concat("SERVER_PORT", listen_port))) + i++; + if ((env[i] = concat("REQUEST_METHOD", req->type))) + i++; + if ((env[i] = concat("PATH_INFO", req->path))) + i++; + if ((env[i] = concat("PATH_TRANSLATED", path))) + i++; + if ((env[i] = concat("SCRIPT_NAME", filename))) + i++; + if ((env[i++] = concat("REMOTE_ADDR", req->peerhost))) + i++; + if ((env[i] = concat("QUERY_STRING", query))) + i++; + env[i] = NULL; + +/* +! The revision of the CGI specification. Format: CGI/revision +! GATEWAY_INTERFACE +! The name and revision of information protcol. Format: protocol/revision +! SERVER_PROTOCOL +! scripts can be accessed by virtual pathname The extra information is sent as +! PATH_INFO. This information should be decoded by server +! if it comes from URL before it is passed to the CGI script. +! PATH_INFO +! AUTH_TYPE +! REMOTE_USER = NULL; +! REMOTE_IDENT = NULL; +! CONTENT_TYPE +! CONTENT_LENGTH +HTTP_ACCEPT = hdr(HTTP_ACCEPT); +HTTP_USER_AGENT = hdr(HTTP_ACCEPT); +*/ + return env; +} + +char *concat(char *key, char *value) +{ + char *s=NULL; + int len=0; + + if (!value || !key) + return NULL; + + len = strlen(key)+strlen(value)+2; + s = (char *) malloc (len); + if (!s) + return NULL; + + memset(s, 0, len); + strcat(s,key); + strcat(s,"="); + strcat(s,value); + + return s; +} diff -u -r -N webfs-orig/contrib/whitelist.pl webfs/contrib/whitelist.pl --- webfs-orig/contrib/whitelist.pl Thu Jan 1 01:00:00 1970 +++ webfs/contrib/whitelist.pl Tue Sep 25 17:48:14 2001 @@ -0,0 +1,17 @@ +#!/usr/bin/perl -w + +use strict; + +print "{0"; +for (my $i=1; $i<255; $i++) +{ + printf ", "; + if (chr($i) =~ /[a-zA-Z0-9\-\.\+_,]/) + { + print 1; + } else { + print 0; + } +} +print "};\n"; +exit; diff -u -r -N webfs-orig/httpd.h webfs/httpd.h --- webfs-orig/httpd.h Fri May 18 11:35:58 2001 +++ webfs/httpd.h Wed Sep 26 23:51:08 2001 @@ -100,6 +100,11 @@ extern int lifespan; extern time_t now; +int cgi(struct REQUEST *req); +char **build_env(struct REQUEST *req, char *filename, char *path, char *query); +char *concat(char *key, char *value); + + void xperror(int loglevel, char *txt, char *peerhost); void xerror(int loglevel, char *txt, char *peerhost); diff -u -r -N webfs-orig/main.c webfs/main.c --- webfs-orig/main.c Sun Jun 24 10:58:48 2001 +++ webfs/main.c Tue Sep 25 23:58:11 2001 @@ -35,6 +35,7 @@ int tcp_port = 0; int max_dircache = 128; char *doc_root = "."; +char *cgi_dir = NULL; char *indexhtml = NULL; char *listen_ip = NULL; char *listen_port = "8000"; @@ -578,7 +579,7 @@ /* parse options */ for (;;) { if (-1 == (c = getopt(argc,argv,"hvsdF" - "r:R:f:p:n:i:t:c:a:u:g:l:L:m:y:b:k:e:"))) + "r:C:R:f:p:n:i:t:c:a:u:g:l:L:m:y:b:k:e:"))) break; switch (c) { case 'h': @@ -602,6 +603,9 @@ case 'r': doc_root = optarg; break; + case 'C': + cgi_dir = optarg; + break; case 'f': indexhtml = optarg; break; diff -u -r -N webfs-orig/request.c webfs/request.c --- webfs-orig/request.c Tue Apr 10 11:31:42 2001 +++ webfs/request.c Mon Oct 1 21:26:57 2001 @@ -11,11 +11,14 @@ #include #include #include +#include #include "httpd.h" /* ---------------------------------------------------------------------- */ +extern char *cgi_dir; + void read_request(struct REQUEST *req, int pipelined) { @@ -397,13 +400,61 @@ return; } - h = filename -1 +sprintf(filename,"%s%s%s%s", - do_chroot ? "" : doc_root, - virtualhosts ? "/" : "", - virtualhosts ? req->hostname : "", - req->path); - if (*h == '/') { + /* support for cgi scripts. + done by Teenage Mutant Ninja Hero Coders - c0re.jp */ + if (!strncmp(req->path, "/cgi-bin", 8) && cgi_dir != NULL) /* client requested cgi-bin dir */ + { + if (req->path[strlen(req->path)-1] == '/') /* do not allow directory browsing in /cgi-bin */ + { + mkerror(req,403,1); + return; + } else + { + cgi(req); + return; + } + } else + + /* user directory translation */ + /* done by Teenage Mutant Ninja Hero Coders - c0re.jp */ + if (req->path[1] == '~') + { + struct passwd *pw_user=NULL; + char user[512]; + int i; + + memset(user, 0, 512); + for (i=2;ipath);i++) + { + if (req->path[i] == '/') + break; + user[i-2] = req->path[i]; + } + + if ((pw_user = getpwnam(user))) + { + strcpy(filename,pw_user->pw_dir); + if (filename[strlen(filename)-1] == '/') + strcat(filename, "public_html"); + else + strcat(filename, "/public_html"); + strcat(filename, strchr(req->path+1, '/')); + } else { + mkerror(req,404,1); + return; + } + h = filename+strlen(filename)-1; + } else + h = filename -1 +sprintf(filename,"%s%s%s%s", + do_chroot ? "" : doc_root, + virtualhosts ? "/" : "", + virtualhosts ? req->hostname : "", + req->path); + + + if (*h == '/') { /* looks like the client asks for a directory */ + if (indexhtml) { /* check for index file */ strcpy(h+1,indexhtml); @@ -418,9 +469,9 @@ mkerror(req,403,1); return; } - } + } } - + if (-1 == stat(filename,&(req->bst))) { if (errno == EACCES) { mkerror(req,403,1);