Various fnord patches
[email protected] Mon, 24 Oct 2005 22:52:18 +0200
| Newsgroups | gmane.comp.web.fnord |
|---|---|
| Message-ID | <[email protected]> |
--YZ5djTAD1cGYuMQK Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit Hi, I have attached some patches that fix a few issues that I've run into with fnord. 1. Incomplete redirect response fnord constructs redirect responses like this: HTTP/1.0 301 Go Away Connection: close Location: <target> In particular, it does not set a Content-Length header. Normally, this is not a problem, because fnord exits after sending the response, causing the connection to shut down. However, when fnord is running with ucspi-ssl, fnord exiting doesn't cause the connection to close, causing the client to wait indefinitely for the response body. I don't know if that's a bug in ucspi-ssl, but it can be easily fixed in fnord by adding a "Content-Length: 0" header. Which is what my patch does. 2. Missing sanitation of request headers fnord looks for a selection of request headers and stores pointers to the header value in certain variables (I've found ua, refer, accept_enc, cookie, auth_type, content_type, content_len). If, in a keep-alive connection, subsequent requests contain a different set of http headers, some of these variables may not be reset to NULL but instead point somewhere into the request buffer. So far, this has only caused a minor annoyance - my referrer logs are getting messed up. But I think it could be used in a request smuggling attack if someone managed to fake a content-length header this way. The problem is caused by lines like this one: if ((tmp=header(buf,len,"Referer"))) refer=tmp; I'm not sure why the return code of header() is checked here, so I've patched all the cases that I've found with refer=header(buf,len,"Referer"); which resets the refer variable to NULL when no referer header is present. 3. Missing mime-type fnord doesn't recognise .ico files. No big deal - but my girlfriend wants a favicon.ico on her web site, so I've added a mime-type for it. 4. Redirect with slash on directory request Maybe fnord doesn't do this on purpose, but I like it simply because it is convenient: If a request to "<domain>/<path>" is received and "<domain>/<path>" is a local directory, fnord originally responds with "not found". My patch causes it to respond with a redirect to "/<path>/" instead, so the directory index page is shown. 5. Logging in case of Encoding: gzip If a request to "<domain>/<path>" is received and "<domain>/<path>" exists and is a readable file and "<domain>/<path>.gz" exists and is readable as well, fnord will serve "<domain>/<path>.gz" if the client accepts gzip encoding. Which is a nice feature to save bandwidth. What I don't like is that fnord logs the name of the file that's served, not the name of the requested file. This is also nothing serious, but it's messing up my log files (most html files are reported twice, once with and once without ".gz"). This cannot be solved by post-processing the log file, because there is no way to tell if the client originally requested "<path>" or "<path>.gz". Therefore I have created a patch to make fnord log the requested filename instead of the name of the served file. 6. Another potential request smuggling problem While reading the request fnord will read up to MAXHEADERLEN - 5 bytes from its input in a single read() call. The buffer is then checked if it contains an empty line, and if so, the buffer content before the empty line is treated as an HTTP request. So far so good. If it's a keep-alive connection, the rest of the buffer is discarded before fnord tries reading the next request. Not good. I found the problem when I tried reproducing problem #2 above by piping an input file containing two requests into fnord. fnord always only sent one response. In practice this may not be a problem, unless a client tries really hard to pipeline requests. I haven't patched this one, but I think it should be. Bye, Peter -- Peter Conrad | "There's nothing wrong with me - maybe there's something Bahnhofstraße 150 | wrong with the universe!" 63263 Neu-Isenburg| - B. Crusher, Star Trek, "Remember Me" Germany email: [email protected] / WWW: http://www.unix-ag.uni-kl.de/~conrad --YZ5djTAD1cGYuMQK Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="fnord-1.10-cl0redirect-1.patch" diff -rU3 fnord-1.10/httpd.c fnord-cl0/httpd.c --- fnord-1.10/httpd.c 2005-08-03 13:32:50.000000000 +0200 +++ fnord-cl0/httpd.c 2005-10-23 18:03:14.000000000 +0200 @@ -880,7 +880,7 @@ } static void redirectboilerplate() { - buffer_puts(buffer_1,"HTTP/1.0 301 Go Away\r\nConnection: close\r\nLocation: "); + buffer_puts(buffer_1,"HTTP/1.0 301 Go Away\r\nConnection: close\r\nContent-Length: 0\r\nLocation: "); } static void handleredirect(const char *url,const char* origurl) { --YZ5djTAD1cGYuMQK Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="fnord-1.10-headerfix-1.patch" diff -rU3 fnord-1.10/httpd.c fnord-hdrfix/httpd.c --- fnord-1.10/httpd.c 2005-08-03 13:32:50.000000000 +0200 +++ fnord-hdrfix/httpd.c 2005-10-23 17:59:43.000000000 +0200 @@ -1362,9 +1362,9 @@ { char *tmp; - if ((tmp=header(buf,len,"User-Agent"))) ua=tmp; - if ((tmp=header(buf,len,"Referer"))) refer=tmp; - if ((tmp=header(buf,len,"Accept-Encoding"))) accept_enc=tmp; + ua=header(buf,len,"User-Agent"); + refer=header(buf,len,"Referer"); + accept_enc=header(buf,len,"Accept-Encoding"); #ifdef KEEPALIVE if ((tmp=header(buf,len,"Connection"))) { /* see if it's "keep-alive" or "close" */ if (!strcasecmp(tmp,"keep-alive")) @@ -1374,13 +1374,13 @@ } #endif #ifdef CGI - if ((tmp=header(buf,len,"Cookie"))) cookie=tmp; - if ((tmp=header(buf,len,"Authorization"))) auth_type=tmp; + cookie=header(buf,len,"Cookie"); + auth_type=header(buf,len,"Authorization"); if (method==POST) { - if ((tmp=header(buf,len,"Content-Type"))) content_type=tmp; - if ((tmp=header(buf,len,"Content-Length"))) content_len=tmp; - if (tmp) { - scan_ulong(tmp,&post_len); + content_type=header(buf,len,"Content-Type"); + content_len=header(buf,len,"Content-Length"); + if (content_len) { + scan_ulong(content_len,&post_len); post_miss=buf+len+1; post_mlen=in-len-1; if (post_len<=post_mlen) post_mlen=post_len; --YZ5djTAD1cGYuMQK Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="fnord-1.10-mimetypes-1.patch" diff -rU3 fnord-1.10/httpd.c fnord-mime/httpd.c --- fnord-1.10/httpd.c 2005-08-03 13:32:50.000000000 +0200 +++ fnord-mime/httpd.c 2005-10-23 18:03:53.000000000 +0200 @@ -694,6 +694,7 @@ { "xbm", "image/x-xbitmap" }, { "xpm", "image/x-xpixmap" }, { "xwd", "image/x-xwindowdump" }, + { "ico", "image/x-icon" }, { 0 } }; /* try to find out MIME type and content encoding. --YZ5djTAD1cGYuMQK Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="fnord-1.10-redirectslash-1.patch" diff -rU3 fnord-1.10/httpd.c fnord-rds/httpd.c --- fnord-1.10/httpd.c 2005-08-03 13:32:50.000000000 +0200 +++ fnord-rds/httpd.c 2005-10-23 18:04:06.000000000 +0200 @@ -903,6 +903,21 @@ buffer_flush(buffer_1); exit(0); } + { + struct stat sbuf; + if (stat(url, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)) { + redirectboilerplate(); + /* This is not quite correct. Redirect URLs must be absolute. + * Works nicely, though. */ + buffer_puts(buffer_1,"/"); + buffer_puts(buffer_1,url); + buffer_puts(buffer_1,"/\r\n\r\n"); + retcode=301; + dolog(0); + buffer_flush(buffer_1); + exit(0); + } + } #ifdef OLD_STYLE_REDIRECT if ((env=getenv("REDIRECT_HOST"))) { redirectboilerplate(); --YZ5djTAD1cGYuMQK Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="fnord-1.10-loggz-1.patch" diff -rU3 fnord-1.10/httpd.c fnord-log/httpd.c --- fnord-1.10/httpd.c 2005-08-03 13:32:50.000000000 +0200 +++ fnord-log/httpd.c 2005-10-23 18:03:40.000000000 +0200 @@ -1568,7 +1568,6 @@ str_copy(fnord+i,".gz"); fd2=doit(buf,len,fnord,0); if (fd2>=0) { /* yeah! */ - url=fnord; close(fd); fd=fd2; } else { @@ -1577,6 +1576,9 @@ } retcode=200; dolog(st.st_size); + if (fd2>=0) { /* yeah! */ + url=fnord; + } if (rangestart || rangeend!=st.st_size) buffer_puts(buffer_1,"HTTP/1.0 206 Partial Content\r\nServer: "FNORD"\r\nContent-Type: "); else --YZ5djTAD1cGYuMQK--