Module Name:    src
Committed By:   shm
Date:           Wed Oct 28 09:20:15 UTC 2015

Modified Files:
        src/libexec/httpd: CHANGES auth-bozo.c bozohttpd.8 bozohttpd.c
            bozohttpd.h cgi-bozo.c dir-index-bozo.c main.c tilde-luzah-bozo.c
        src/libexec/httpd/lua: bozo.lua

Log Message:
* add CGI support for ~user translation (-E switch)
* add redirects to ~user translation
* fix bugs around ~user translation
* add schema detection for absolute redirects
* fixed few memory leaks
* bunch of minor tweaks
* removed -r support
* smarter redirects

OK mrg@


To generate a diff of this commit:
cvs rdiff -u -r1.20 -r1.21 src/libexec/httpd/CHANGES
cvs rdiff -u -r1.16 -r1.17 src/libexec/httpd/auth-bozo.c
cvs rdiff -u -r1.53 -r1.54 src/libexec/httpd/bozohttpd.8
cvs rdiff -u -r1.66 -r1.67 src/libexec/httpd/bozohttpd.c
cvs rdiff -u -r1.37 -r1.38 src/libexec/httpd/bozohttpd.h
cvs rdiff -u -r1.27 -r1.28 src/libexec/httpd/cgi-bozo.c
cvs rdiff -u -r1.21 -r1.22 src/libexec/httpd/dir-index-bozo.c
cvs rdiff -u -r1.8 -r1.9 src/libexec/httpd/main.c
cvs rdiff -u -r1.11 -r1.12 src/libexec/httpd/tilde-luzah-bozo.c
cvs rdiff -u -r1.1.1.1 -r1.2 src/libexec/httpd/lua/bozo.lua

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/libexec/httpd/CHANGES
diff -u src/libexec/httpd/CHANGES:1.20 src/libexec/httpd/CHANGES:1.21
--- src/libexec/httpd/CHANGES:1.20	Fri Mar 20 19:54:53 2015
+++ src/libexec/httpd/CHANGES	Wed Oct 28 09:20:15 2015
@@ -1,5 +1,15 @@
 $eterna: CHANGES,v 1.78 2011/11/18 01:25:11 mrg Exp $
 
+changes in bozohttpd 20151028:
+	o  add CGI support for ~user translation (-E switch)
+	o  add redirects to ~user translation
+	o  fix bugs around ~user translation
+	o  add schema detection for absolute redirects
+	o  fixed few memory leaks
+	o  bunch of minor tweaks
+	o  removed -r support
+	o  smarter redirects 
+
 changes in bozohttpd 20150320:
 	o  fix redirection handling
 	o  support transport stream (.ts) and video object (.vob) files

Index: src/libexec/httpd/auth-bozo.c
diff -u src/libexec/httpd/auth-bozo.c:1.16 src/libexec/httpd/auth-bozo.c:1.17
--- src/libexec/httpd/auth-bozo.c:1.16	Fri Dec 26 19:52:00 2014
+++ src/libexec/httpd/auth-bozo.c	Wed Oct 28 09:20:15 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: auth-bozo.c,v 1.16 2014/12/26 19:52:00 mrg Exp $	*/
+/*	$NetBSD: auth-bozo.c,v 1.17 2015/10/28 09:20:15 shm Exp $	*/
 
 /*	$eterna: auth-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -190,8 +190,7 @@ bozo_auth_check_401(bozo_httpreq_t *requ
 	if (code == 401)
 		bozo_printf(httpd,
 			"WWW-Authenticate: Basic realm=\"%s\"\r\n",
-			(request && request->hr_authrealm) ?
-				request->hr_authrealm : "default realm");
+			request->hr_authrealm ? request->hr_authrealm : "default realm");
 }
 
 #ifndef NO_CGIBIN_SUPPORT

Index: src/libexec/httpd/bozohttpd.8
diff -u src/libexec/httpd/bozohttpd.8:1.53 src/libexec/httpd/bozohttpd.8:1.54
--- src/libexec/httpd/bozohttpd.8:1.53	Thu Aug 13 12:30:08 2015
+++ src/libexec/httpd/bozohttpd.8	Wed Oct 28 09:20:15 2015
@@ -1,4 +1,4 @@
-.\"	$NetBSD: bozohttpd.8,v 1.53 2015/08/13 12:30:08 wiz Exp $
+.\"	$NetBSD: bozohttpd.8,v 1.54 2015/10/28 09:20:15 shm Exp $
 .\"
 .\"	$eterna: bozohttpd.8,v 1.101 2011/11/18 01:25:11 mrg Exp $
 .\"
@@ -111,9 +111,11 @@ is a valid CGI program in the
 directory.
 In other words, all CGI URL's must begin with
 .Em \%/cgi-bin/ .
-Note that the CGI/1.1 interface is not available with
+Note that the CGI/1.1 interface is available with
 .Em ~user
-translation.
+translation using
+.Fl E
+switch.
 .It Fl e
 Causes
 .Nm
@@ -223,15 +225,6 @@ translations from
 .Dq public_html
 to
 .Ar pubdir .
-.It Fl r
-Forces pages besides the
-.Dq index.html
-(see the
-.Fl X
-option) page to require that the Referrer: header be present and
-refer to this web server, otherwise a redirect to the
-.Dq index.html
-page will be returned instead.
 .It Fl S Ar server_software
 Sets the internal server version to
 .Ar server_software .
@@ -270,6 +263,12 @@ into the directory
 (but see the
 .Fl p
 option above).
+.It Fl E
+Enables CGI/1.1 interface for
+.Em ~user
+translation.
+Note that enabling this support implies that users can run
+commands as web server user, this may have security implications.
 .It Fl V
 Sets the default virtual host directory to
 .Ar slashdir .
@@ -462,12 +461,23 @@ symbolic link is found,
 .Nm
 will perform a smart redirect to the target of this symlink.
 The target is assumed to live on the same server.
+If target starts with slash then absolute redirection is performed,
+otherwise it's handled as relative.
 If a
 .Pa .bzabsredirect
 symbolic link is found,
 .Nm
 will redirect to the absolute url pointed to by this symlink.
 This is useful to redirect to different servers.
+Two forms of redirection are supported - symbolic link without schema will use
+.Em http://
+as default i.e. link to
+.Em NetBSD.org
+will redirect to
+.Em http://NetBSD.org/
+Otherwise provided schema will be used i.e. symbolic link to
+.Em ftp://NetBSD.org/
+will redirect to provided the URL.
 .Sh EXAMPLES
 To configure set of virtual hosts, one would use an
 .Xr inetd.conf 5
@@ -613,7 +623,7 @@ provided man page fixes
 .Aq Mt [email protected]
 Added the
 .Fl P
-option.
+option (pidfile support) and provided some man page fixes.
 .It
 .An Luke Mewburn
 .Aq Mt [email protected]
@@ -666,9 +676,6 @@ provided http authorisation fixes
 provided chroot and change-to-user support, and other various fixes
 .It
 Coyote Point provided various CGI fixes.
-.It
-.An Julio Merino
-added pidfile support and provided some man page fixes.
 .El
 .Pp
 There are probably others I have forgotten (let me know if you care)

Index: src/libexec/httpd/bozohttpd.c
diff -u src/libexec/httpd/bozohttpd.c:1.66 src/libexec/httpd/bozohttpd.c:1.67
--- src/libexec/httpd/bozohttpd.c:1.66	Thu Jul 16 12:19:23 2015
+++ src/libexec/httpd/bozohttpd.c	Wed Oct 28 09:20:15 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: bozohttpd.c,v 1.66 2015/07/16 12:19:23 shm Exp $	*/
+/*	$NetBSD: bozohttpd.c,v 1.67 2015/10/28 09:20:15 shm Exp $	*/
 
 /*	$eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -341,6 +341,7 @@ bozo_clean_request(bozo_httpreq_t *reque
 	free(request->hr_oldfile);
 	free(request->hr_query);
 	free(request->hr_host);
+	bozo_user_free(request->hr_user);
 	bozo_auth_cleanup(request);
 	for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr;
 	    hdr = SIMPLEQ_NEXT(hdr, h_next)) {
@@ -851,10 +852,11 @@ parse_http_date(const char *val, time_t 
 /*
  * given an url, encode it ala rfc 3986.  ie, escape ? and friends.
  * note that this function returns a static buffer, and thus needs
- * to be updated for any sort of parallel processing.
+ * to be updated for any sort of parallel processing. escape only
+ * chosen characters for absolute redirects
  */
 char *
-bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url)
+bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url, int absolute)
 {
 	static char *buf;
 	static size_t buflen = 0;
@@ -868,11 +870,6 @@ bozo_escape_rfc3986(bozohttpd_t *httpd, 
 		buf = bozorealloc(httpd, buf, buflen);
 	}
 
-	if (url == NULL) {
-		buf[0] = 0;
-		return buf;
-	}
-
 	for (len = 0, s = url, d = buf; *s;) {
 		if (*s & 0x80)
 			goto encode_it;
@@ -895,15 +892,18 @@ bozo_escape_rfc3986(bozohttpd_t *httpd, 
 		case ';':
 		case '=':
 		case '%':
+		case '"':
+			if (absolute)
+				goto leave_it;
 		case '\n':
 		case '\r':
 		case ' ':
-		case '"':
 		encode_it:
 			snprintf(d, 4, "%%%02X", *s++);
 			d += 3;
 			len += 3;
 			break;
+		leave_it:
 		default:
 			*d++ = *s++;
 			len++;
@@ -916,56 +916,24 @@ bozo_escape_rfc3986(bozohttpd_t *httpd, 
 }
 
 /*
- * checks to see if this request has a valid .bzdirect file.  returns
- * 0 on failure and 1 on success.
- */
-static int
-check_direct_access(bozo_httpreq_t *request)
-{
-	FILE *fp;
-	struct stat sb;
-	char dir[MAXPATHLEN], dirfile[MAXPATHLEN], *basename;
-
-	snprintf(dir, sizeof(dir), "%s", request->hr_file + 1);
-	debug((request->hr_httpd, DEBUG_FAT, "check_direct_access: dir %s", dir));
-	basename = strrchr(dir, '/');
-
-	if ((!basename || basename[1] != '\0') &&
-	    lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode))
-		/* nothing */;
-	else if (basename == NULL)
-		strcpy(dir, ".");
-	else {
-		*basename++ = '\0';
-		bozo_check_special_files(request, basename);
-	}
-
-	if ((size_t)snprintf(dirfile, sizeof(dirfile), "%s/%s", dir,
-	  DIRECT_ACCESS_FILE) >= sizeof(dirfile)) {
-		bozo_http_error(request->hr_httpd, 404, request,
-		  "directfile path too long");
-		return 0;
-	}
-	if (stat(dirfile, &sb) < 0 ||
-	    (fp = fopen(dirfile, "r")) == NULL)
-		return 0;
-	fclose(fp);
-	return 1;
-}
-
-/*
- * do automatic redirection -- if there are query parameters for the URL
- * we will tack these on to the new (redirected) URL.
+ * do automatic redirection -- if there are query parameters or userdir for
+ * the URL we will tack these on to the new (redirected) URL.
  */
 static void
 handle_redirect(bozo_httpreq_t *request,
 		const char *url, int absolute)
 {
 	bozohttpd_t *httpd = request->hr_httpd;
-	char *urlbuf;
+	char *finalurl, *urlbuf;
+#ifndef NO_USER_SUPPORT
+	char *userbuf;
+#endif /* !NO_USER_SUPPORT */
 	char portbuf[20];
 	const char *hostname = BOZOHOST(httpd, request);
+	size_t finalurl_len;
 	int query = 0;
+	int absproto = 0; /* absolute redirect provides own schema
+			   * eg. https:// */
 
 	if (url == NULL) {
 		if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0)
@@ -973,7 +941,46 @@ handle_redirect(bozo_httpreq_t *request,
 		url = urlbuf;
 	} else
 		urlbuf = NULL;
-	url = bozo_escape_rfc3986(request->hr_httpd, url);
+
+#ifndef NO_USER_SUPPORT
+	if (request->hr_user && !absolute) {
+		if (asprintf(&userbuf, "/~%s%s", request->hr_user, url) < 0)
+			bozo_err(httpd, 1, "asprintf");
+		url = userbuf;
+	} else
+		userbuf = NULL;
+#endif /* !NO_USER_SUPPORT */
+
+	if (absolute) {
+		char *sep = NULL;
+		const char *s;
+
+		/*
+		 * absolute redirect may specify own protocol i.e. to redirect to
+		 * another schema like https:// or ftp://. Details: RFC 3986, section
+		 * 3.
+		 */
+
+		/* 1. check if url contains :// */
+		sep = strstr(url, "://");
+
+		/*
+		 * RFC 3986, section 3.1:
+		 * scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+		 */
+		if (sep != NULL) {
+			for (s = url; s != sep;) {
+				if (!isalnum((int)*s) && *s != '+' && *s != '-' &&
+					*s != '.')
+					break;
+				if (++s == sep) {
+					absproto = 1;
+				}
+			}
+		}
+	}
+
+	url = bozo_escape_rfc3986(request->hr_httpd, url, absolute);
 
 	if (request->hr_query && strlen(request->hr_query))
 		query = 1;
@@ -983,48 +990,62 @@ handle_redirect(bozo_httpreq_t *request,
 		    request->hr_serverport);
 	else
 		portbuf[0] = '\0';
-	if (absolute)
-		bozo_warn(httpd, "redirecting %s", url);
-	else
-		bozo_warn(httpd, "redirecting %s%s%s", hostname, portbuf, url);
-	debug((httpd, DEBUG_FAT, "redirecting %s", url));
+
+	/* construct final redirection url */
+	finalurl_len = strlen(url) + 1;
+	if (!absproto) {
+		/* add default schema */
+		if (httpd->sslinfo)
+			finalurl_len += sizeof("https://";) - 1;
+		else
+			finalurl_len += sizeof("http://";) - 1;
+	}
+	if (absolute == 0)
+		finalurl_len += strlen(hostname)+strlen(portbuf);
+	if (query)
+		finalurl_len += strlen(request->hr_query) + 1; /* byte more for ? */
+	finalurl = bozomalloc(httpd, finalurl_len);
+	strcpy(finalurl, "");
+	if (!absproto) {
+		/* add default schema */
+		if (httpd->sslinfo)
+			strlcat(finalurl, "https://";, finalurl_len);
+		else
+			strlcat(finalurl, "http://";, finalurl_len);
+	}
+	if (absolute == 0) {
+		strlcat(finalurl, hostname, finalurl_len);
+		strlcat(finalurl, portbuf, finalurl_len);
+	}
+	strlcat(finalurl, url, finalurl_len);
+	if (query) {
+		strlcat(finalurl, "?", finalurl_len);
+		strlcat(finalurl, request->hr_query, finalurl_len);
+	}
+
+	bozo_warn(httpd, "redirecting %s", finalurl);
+	debug((httpd, DEBUG_FAT, "redirecting %s", finalurl));
+
 	bozo_printf(httpd, "%s 301 Document Moved\r\n", request->hr_proto);
 	if (request->hr_proto != httpd->consts.http_09)
 		bozo_print_header(request, NULL, "text/html", NULL);
-	if (request->hr_proto != httpd->consts.http_09) {
-		bozo_printf(httpd, "Location: http://";);
-		if (absolute == 0)
-			bozo_printf(httpd, "%s%s", hostname, portbuf);
-		if (query) {
-			bozo_printf(httpd, "%s?%s\r\n", url, request->hr_query);
-		} else {
-			bozo_printf(httpd, "%s\r\n", url);
-		}
-	}
+	if (request->hr_proto != httpd->consts.http_09)
+		bozo_printf(httpd, "Location: %s\r\n", finalurl);
 	bozo_printf(httpd, "\r\n");
 	if (request->hr_method == HTTP_HEAD)
 		goto head;
 	bozo_printf(httpd, "<html><head><title>Document Moved</title></head>\n");
 	bozo_printf(httpd, "<body><h1>Document Moved</h1>\n");
-	bozo_printf(httpd, "This document had moved <a href=\"http://";);
-	if (query) {
-		if (absolute)
-			bozo_printf(httpd, "%s?%s", url, request->hr_query);
-		else
-			bozo_printf(httpd, "%s%s%s?%s", hostname,
-				    portbuf, url, request->hr_query);
-	} else {
-		if (absolute)
-			bozo_printf(httpd, "%s", url);
-		else
-			bozo_printf(httpd, "%s%s%s", hostname,
-				    portbuf, url);
-	}
-	bozo_printf(httpd, "\">here</a>\n");
+	bozo_printf(httpd, "This document had moved <a href=\"%s\">here</a>\n",
+	  finalurl);
 	bozo_printf(httpd, "</body></html>\n");
 head:
 	bozo_flush(httpd, stdout);
 	free(urlbuf);
+	free(finalurl);
+#ifndef NO_USER_SUPPORT
+	free(userbuf);
+#endif /* !NO_USER_SUPPORT */
 }
 
 /*
@@ -1041,9 +1062,6 @@ check_virtual(bozo_httpreq_t *request)
 	char *file = request->hr_file, *s;
 	size_t len;
 
-	if (!httpd->virtbase)
-		goto use_slashdir;
-
 	/*
 	 * convert http://virtual.host/ to request->hr_host
 	 */
@@ -1073,6 +1091,29 @@ check_virtual(bozo_httpreq_t *request)
 		request->hr_host[len - 3] = '\0';
 		len = strlen(request->hr_host);
 	}
+
+	if (!httpd->virtbase) {
+
+		/*
+		 * if we don't use vhost support, then set virthostname if
+		 * user supplied Host header. It will be used for possible
+		 * redirections
+		 */
+
+		if (request->hr_host) {
+			s = strrchr(request->hr_host, ':');
+			if (s != NULL)
+				/* truncate Host: as we want to copy it without port part */
+				*s = '\0';
+			request->hr_virthostname = bozostrdup(request->hr_httpd,
+			  request->hr_host);
+			if (s != NULL)
+				/* fix Host: again, if we truncated it */
+				*s = ':';
+		}
+
+		goto use_slashdir;
+	}
 	
 	/*
 	 * ok, we have a virtual host, use opendir(3) to find a case
@@ -1161,16 +1202,20 @@ check_bzredirect(bozo_httpreq_t *request
 	basename = strrchr(dir, '/');
 
 	if ((!basename || basename[1] != '\0') &&
-	    lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode))
-		/* nothing */;
-	else if (basename == NULL)
-		strcpy(dir, ".");
-	else {
+	    lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+		strcpy(path, dir);
+	} else if (basename == NULL) {
+		strcpy(path, ".");
+		strcpy(dir, "");
+	} else {
 		*basename++ = '\0';
 		bozo_check_special_files(request, basename);
+		strcpy(path, dir);
 	}
 
-	if ((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir,
+	debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: path %s", path));
+
+	if ((size_t)snprintf(redir, sizeof(redir), "%s/%s", path,
 	  REDIRECT_FILE) >= sizeof(redir)) {
 		bozo_http_error(request->hr_httpd, 404, request,
 		  "redirectfile path too long");
@@ -1181,7 +1226,7 @@ check_bzredirect(bozo_httpreq_t *request
 			return 0;
 		absolute = 0;
 	} else {
-		if((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir,
+		if((size_t)snprintf(redir, sizeof(redir), "%s/%s", path,
 		  ABSREDIRECT_FILE) >= sizeof(redir)) {
 			bozo_http_error(request->hr_httpd, 404, request,
 			  "redirectfile path too long");
@@ -1208,16 +1253,15 @@ check_bzredirect(bozo_httpreq_t *request
 		return 1;
 
 	/* now we have the link pointer, redirect to the real place */
-	if (absolute)
-		finalredir = redirpath;
-	else {
-		if ((size_t)snprintf(finalredir = redir, sizeof(redir), "/%s/%s",
-		  dir, redirpath) >= sizeof(redir)) {
+	if (!absolute && redirpath[0] != '/') {
+		if ((size_t)snprintf(finalredir = redir, sizeof(redir), "%s%s/%s",
+		  (strlen(dir) > 0 ? "/" : ""), dir, redirpath) >= sizeof(redir)) {
 			bozo_http_error(request->hr_httpd, 404, request,
 			  "redirect path too long");
 			return -1;
 		}
-	}
+	} else
+		finalredir = redirpath;
 
 	debug((request->hr_httpd, DEBUG_FAT,
 	       "check_bzredirect: new redir %s", finalredir));
@@ -1300,7 +1344,6 @@ fix_url_percent(bozo_httpreq_t *request)
  * transform_request does this:
  *	- ``expand'' %20 crapola
  *	- punt if it doesn't start with /
- *	- check httpd->untrustedref / referrer
  *	- look for "http://myname/"; and deal with it.
  *	- maybe call bozo_process_cgi()
  *	- check for ~user and call bozo_user_transform() if so
@@ -1318,7 +1361,6 @@ transform_request(bozo_httpreq_t *reques
 	bozohttpd_t *httpd = request->hr_httpd;
 	char	*file, *newfile = NULL;
 	size_t	len;
-	const char *hostname = BOZOHOST(httpd, request);
 
 	file = NULL;
 	*isindex = 0;
@@ -1340,84 +1382,46 @@ transform_request(bozo_httpreq_t *reques
 	while (file[1] == '/')
 		file++;
 
-	switch(check_bzredirect(request)) {
-	case -1:
-		goto bad_done;
-	case 1:
-		return 0;
-	}
-
-	if (httpd->untrustedref) {
-		int to_indexhtml = 0;
-
-#define TOP_PAGE(x)	(strcmp((x), "/") == 0 || \
-			 strcmp((x) + 1, httpd->index_html) == 0 || \
-			 strcmp((x) + 1, "favicon.ico") == 0)
-
-		debug((httpd, DEBUG_EXPLODING, "checking httpd->untrustedref"));
-		/*
-		 * first check that this path isn't allowed via .bzdirect file,
-		 * and then check referrer; make sure that people come via the
-		 * real name... otherwise if we aren't looking at / or
-		 * /index.html, redirect...  we also special case favicon.ico.
-		 */
-		if (check_direct_access(request))
-			/* nothing */;
-		else if (request->hr_referrer) {
-			const char *r = request->hr_referrer;
-
-			debug((httpd, DEBUG_FAT,
-				"checking referrer \"%s\" vs virthostname %s",
-				r, hostname));
-			if (strncmp(r, "http://";, 7) != 0 ||
-			    (strncasecmp(r + 7, hostname,
-			    		 strlen(hostname)) != 0 &&
-			     !TOP_PAGE(file)))
-				to_indexhtml = 1;
-		} else {
-			const char *h = request->hr_host;
-
-			debug((httpd, DEBUG_FAT, "url has no referrer at all"));
-			/* if there's no referrer, let / or /index.html past */
-			if (!TOP_PAGE(file) ||
-			    (h && strncasecmp(h, hostname,
-			    		strlen(hostname)) != 0))
-				to_indexhtml = 1;
-		}
-
-		if (to_indexhtml) {
-			char *slashindexhtml;
-
-			if (asprintf(&slashindexhtml, "/%s",
-					httpd->index_html) < 0)
-				bozo_err(httpd, 1, "asprintf");
-			debug((httpd, DEBUG_FAT,
-				"httpd->untrustedref: redirecting %s to %s",
-				file, slashindexhtml));
-			handle_redirect(request, slashindexhtml, 0);
-			free(slashindexhtml);
-			return 0;
-		}
-	}
+	/* fix file provided by user as it's used in other handlers */
+	request->hr_file = file;
 
 	len = strlen(file);
-	if (/*CONSTCOND*/0) {
+
 #ifndef NO_USER_SUPPORT
-	} else if (len > 1 && httpd->enable_users && file[1] == '~') {
+	/* first of all expand user path */
+	if (len > 1 && httpd->enable_users && file[1] == '~') {
 		if (file[2] == '\0') {
 			(void)bozo_http_error(httpd, 404, request,
 						"missing username");
 			goto bad_done;
 		}
 		if (strchr(file + 2, '/') == NULL) {
-			handle_redirect(request, NULL, 0);
+			char *userredirecturl;
+			if (asprintf(&userredirecturl, "%s/", file) < 0)
+				bozo_err(httpd, 1, "asprintf");
+			handle_redirect(request, userredirecturl, 0);
+			free(userredirecturl);
 			return 0;
 		}
 		debug((httpd, DEBUG_FAT, "calling bozo_user_transform"));
 
-		return bozo_user_transform(request, isindex);
+		if (!bozo_user_transform(request))
+			return 0;
+		
+		file = request->hr_file;
+		len = strlen(file);
+	}
 #endif /* NO_USER_SUPPORT */
-	} else if (len > 1) {
+
+
+	switch (check_bzredirect(request)) {
+	case -1:
+		goto bad_done;
+	case 1:
+		return 0;
+	}
+
+	if (len > 1) {
 		debug((httpd, DEBUG_FAT, "file[len-1] == %c", file[len-1]));
 		if (file[len-1] == '/') {	/* append index.html */
 			*isindex = 1;
@@ -1444,15 +1448,14 @@ transform_request(bozo_httpreq_t *reques
 	}
 
 	/*
-	 * look for "http://myname/"; and deal with it as necessary.
-	 */
-
-	/*
 	 * stop traversing outside our domain
 	 *
 	 * XXX true security only comes from our parent using chroot(2)
 	 * before execve(2)'ing us.  or our own built in chroot(2) support.
 	 */
+	
+	debug((httpd, DEBUG_FAT, "newfile: %s", newfile));
+	
 	if (*newfile == '/' || strcmp(newfile, "..") == 0 ||
 	    strstr(newfile, "/..") || strstr(newfile, "../")) {
 		(void)bozo_http_error(httpd, 403, request, "illegal request");
@@ -1570,7 +1573,7 @@ bozo_process_request(bozo_httpreq_t *req
 
 	if (fd < 0) {
 		debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno)));
-		switch(errno) {
+		switch (errno) {
 		case EPERM:
 		case EACCES:
 			(void)bozo_http_error(httpd, 403, request,
@@ -1649,9 +1652,6 @@ bozo_process_request(bozo_httpreq_t *req
 		while (szleft) {
 			size_t sz;
 
-			/* This should take care of the first unaligned chunk */
-			if ((cur_byte_pos & (httpd->page_size - 1)) != 0)
-				sz = (size_t)(cur_byte_pos & ~httpd->page_size);
 			if ((off_t)httpd->mmapsz < szleft)
 				sz = httpd->mmapsz;
 			else
@@ -1733,7 +1733,7 @@ bozo_print_header(bozo_httpreq_t *reques
 			len = sbp->st_size;
 		bozo_printf(httpd, "Content-Length: %qd\r\n", (long long)len);
 	}
-	if (request && request->hr_proto == httpd->consts.http_11)
+	if (request->hr_proto == httpd->consts.http_11)
 		bozo_printf(httpd, "Connection: close\r\n");
 	bozo_flush(httpd, stdout);
 }
@@ -1927,26 +1927,53 @@ bozo_http_error(bozohttpd_t *httpd, int 
 		portbuf[0] = '\0';
 
 	if (request && request->hr_file) {
-		char *file = NULL;
+		char *file = NULL, *user = NULL, *user_escaped = NULL;
+		int file_alloc = 0, user_alloc = 0;
 		const char *hostname = BOZOHOST(httpd, request);
 
 		/* bozo_escape_html() failure here is just too bad. */
 		file = bozo_escape_html(NULL, request->hr_file);
-		if (file == NULL)
+		if (file == NULL) 
 			file = request->hr_file;
+		else
+			file_alloc = 1;
+
+#ifndef NO_USER_SUPPORT
+		if (request->hr_user != NULL) {
+			user_escaped = bozo_escape_html(NULL, request->hr_user);
+			if (user_escaped == NULL)
+				user_escaped = request->hr_user;
+			else
+				user_alloc = 1;
+			/* expand username to ~user/ */
+			user = alloca(strlen(user_escaped) + 3);
+			if (user != NULL) {
+				strcpy(user, "~");
+				strcat(user, user_escaped);
+				strcat(user, "/");
+			}
+			if (user_alloc == 1)
+				free(user_escaped);
+		}
+#endif /* !NO_USER_SUPPORT */
+
 		size = snprintf(httpd->errorbuf, BUFSIZ,
 		    "<html><head><title>%s</title></head>\n"
 		    "<body><h1>%s</h1>\n"
-		    "%s: <pre>%s</pre>\n"
+		    "%s%s: <pre>%s</pre>\n"
  		    "<hr><address><a href=\"http://%s%s/\";>%s%s</a></address>\n"
 		    "</body></html>\n",
-		    header, header, file, reason,
-		    hostname, portbuf, hostname, portbuf);
+		    header, header,
+		    user ? user : "", file,
+		    reason, hostname, portbuf, hostname, portbuf);
 		if (size >= (int)BUFSIZ) {
 			bozo_warn(httpd,
 				"bozo_http_error buffer too small, truncated");
 			size = (int)BUFSIZ;
 		}
+
+		if (file_alloc)
+			free(file);
 	} else
 		size = 0;
 
@@ -2207,10 +2234,6 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs
 	    strcmp(cp, "true") == 0) {
 		httpd->numeric = 1;
 	}
-	if ((cp = bozo_get_pref(prefs, "trusted referal")) != NULL &&
-	    strcmp(cp, "true") == 0) {
-		httpd->untrustedref = 1;
-	}
 	if ((cp = bozo_get_pref(prefs, "log to stderr")) != NULL &&
 	    strcmp(cp, "true") == 0) {
 		httpd->logstderr = 1;
@@ -2239,6 +2262,10 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs
 	    strcmp(cp, "true") == 0) {
 		httpd->enable_users = 1;
 	}
+	if ((cp = bozo_get_pref(prefs, "enable user cgibin")) != NULL &&
+	    strcmp(cp, "true") == 0) {
+		httpd->enable_cgi_users = 1;
+	}
 	if ((cp = bozo_get_pref(prefs, "dirty environment")) != NULL &&
 	    strcmp(cp, "true") == 0) {
 		dirtyenv = 1;

Index: src/libexec/httpd/bozohttpd.h
diff -u src/libexec/httpd/bozohttpd.h:1.37 src/libexec/httpd/bozohttpd.h:1.38
--- src/libexec/httpd/bozohttpd.h:1.37	Sun Oct 25 19:06:49 2015
+++ src/libexec/httpd/bozohttpd.h	Wed Oct 28 09:20:15 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: bozohttpd.h,v 1.37 2015/10/25 19:06:49 mrg Exp $	*/
+/*	$NetBSD: bozohttpd.h,v 1.38 2015/10/28 09:20:15 shm Exp $	*/
 
 /*	$eterna: bozohttpd.h,v 1.39 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -94,7 +94,6 @@ typedef struct bozohttpd_t {
 	int		 numeric;	/* avoid gethostby*() */
 	char		*virtbase;	/* virtual directory base */
 	int		 unknown_slash;	/* unknown vhosts go to normal slashdir */
-	int		 untrustedref;	/* make sure referrer = me unless url = / */
 	int		 logstderr;	/* log to stderr (even if not tty) */
 	int		 background;	/* drop into daemon mode */
 	int		 foreground;	/* keep daemon mode in foreground */
@@ -109,6 +108,7 @@ typedef struct bozohttpd_t {
 	const char	*index_html;	/* our home page */
 	const char	*public_html;	/* ~user/public_html page */
 	int		 enable_users;	/* enable public_html */
+	int		 enable_cgi_users;	/* use the cgi handler */
 	int		*sock;		/* bound sockets */
 	int		 nsock;		/* number of above */
 	struct pollfd	*fds;		/* current poll fd set */
@@ -151,6 +151,9 @@ typedef struct bozo_httpreq_t {
 	char	*hr_query;
 	char	*hr_host;	/* HTTP/1.1 Host: or virtual hostname,
 				   possibly including a port number */
+#ifndef NO_USER_SUPPORT
+	char	*hr_user;	/* username if we hit userdir request */
+#endif /* !NO_USER_SUPPORT */
 	const char *hr_proto;
 	const char *hr_content_type;
 	const char *hr_content_length;
@@ -225,7 +228,7 @@ int	bozo_http_error(bozohttpd_t *, int, 
 int	bozo_check_special_files(bozo_httpreq_t *, const char *);
 char	*bozo_http_date(char *, size_t);
 void	bozo_print_header(bozo_httpreq_t *, struct stat *, const char *, const char *);
-char	*bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url);
+char	*bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url, int absolute);
 char	*bozo_escape_html(bozohttpd_t *httpd, const char *url);
 
 char	*bozodgetln(bozohttpd_t *, int, ssize_t *, ssize_t (*)(bozohttpd_t *, int, void *, size_t));
@@ -305,9 +308,11 @@ void	bozo_daemon_closefds(bozohttpd_t *)
 
 /* tilde-luzah-bozo.c */
 #ifdef NO_USER_SUPPORT
-#define bozo_user_transform(a, c)			0
+#define bozo_user_transform(x)				0
+#define bozo_user_free(x)					0
 #else
-int	bozo_user_transform(bozo_httpreq_t *, int *);
+int	bozo_user_transform(bozo_httpreq_t *);
+#define bozo_user_free(x)					free(x)
 #endif /* NO_USER_SUPPORT */
 
 

Index: src/libexec/httpd/cgi-bozo.c
diff -u src/libexec/httpd/cgi-bozo.c:1.27 src/libexec/httpd/cgi-bozo.c:1.28
--- src/libexec/httpd/cgi-bozo.c:1.27	Sat May  2 11:35:48 2015
+++ src/libexec/httpd/cgi-bozo.c	Wed Oct 28 09:20:15 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: cgi-bozo.c,v 1.27 2015/05/02 11:35:48 mrg Exp $	*/
+/*	$NetBSD: cgi-bozo.c,v 1.28 2015/10/28 09:20:15 shm Exp $	*/
 
 /*	$eterna: cgi-bozo.c,v 1.40 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -90,7 +90,7 @@ parse_header(bozohttpd_t *httpd, const c
 	name = bozostrnsep(&value, ":", &len);
 
 	if (NULL == name || -1 == len) {
-		free(name);
+		free(value);
 		return -1;
 	}
 
@@ -260,6 +260,11 @@ bozo_process_cgi(bozo_httpreq_t *request
 	if (!httpd->cgibin && !httpd->process_cgi)
 		return 0;
 
+#ifndef NO_USER_SUPPORT
+    if (request->hr_user && !httpd->enable_cgi_users)
+		return 0;
+#endif /* !NO_USER_SUPPORT */
+
 	if (request->hr_oldfile && strcmp(request->hr_oldfile, "/") != 0)
 		uri = request->hr_oldfile;
 	else

Index: src/libexec/httpd/dir-index-bozo.c
diff -u src/libexec/httpd/dir-index-bozo.c:1.21 src/libexec/httpd/dir-index-bozo.c:1.22
--- src/libexec/httpd/dir-index-bozo.c:1.21	Thu Aug 27 17:12:18 2015
+++ src/libexec/httpd/dir-index-bozo.c	Wed Oct 28 09:20:15 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: dir-index-bozo.c,v 1.21 2015/08/27 17:12:18 mrg Exp $	*/
+/*	$NetBSD: dir-index-bozo.c,v 1.22 2015/10/28 09:20:15 shm Exp $	*/
 
 /*	$eterna: dir-index-bozo.c,v 1.20 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -66,7 +66,7 @@ bozo_dir_index(bozo_httpreq_t *request, 
 	DIR *dp;
 	char buf[MAXPATHLEN];
 	char spacebuf[48];
-	char *file = NULL;
+	char *file = NULL, *printname = NULL;
 	int l, k, j, i;
 
 	if (!isindex || !httpd->dir_indexing)
@@ -108,11 +108,22 @@ bozo_dir_index(bozo_httpreq_t *request, 
 		goto done;
 	}
 
+#ifndef NO_USER_SUPPORT
+	if (request->hr_user) {
+		if (asprintf(&printname, "~%s/%s", request->hr_user,
+		  request->hr_file) < 0)
+			bozo_err(httpd, 1, "asprintf");
+	} else
+		printname = bozostrdup(httpd, request->hr_file);
+#else
+	printname = bozostrdup(httpd, request->hr_file);
+#endif /* !NO_USER_SUPPORT */
+
 	bozo_printf(httpd,
 		"<html><head><title>Index of %s</title></head>\r\n",
-		request->hr_file);
+		printname);
 	bozo_printf(httpd, "<body><h1>Index of %s</h1>\r\n",
-		request->hr_file);
+		printname);
 	bozo_printf(httpd, "<pre>\r\n");
 #define NAMELEN 40
 #define LMODLEN 19
@@ -140,7 +151,7 @@ bozo_dir_index(bozo_httpreq_t *request, 
 
 		l = 0;
 
-		urlname = bozo_escape_rfc3986(httpd, name);
+		urlname = bozo_escape_rfc3986(httpd, name, 0);
 		htmlname = bozo_escape_html(httpd, name);
 		if (htmlname == NULL)
 			htmlname = name;
@@ -206,6 +217,7 @@ bozo_dir_index(bozo_httpreq_t *request, 
 
 done:
 	free(file);
+	free(printname);
 	return 1;
 }
 #endif /* NO_DIRINDEX_SUPPORT */

Index: src/libexec/httpd/main.c
diff -u src/libexec/httpd/main.c:1.8 src/libexec/httpd/main.c:1.9
--- src/libexec/httpd/main.c:1.8	Wed Jul 16 07:41:43 2014
+++ src/libexec/httpd/main.c	Wed Oct 28 09:20:15 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: main.c,v 1.8 2014/07/16 07:41:43 mrg Exp $	*/
+/*	$NetBSD: main.c,v 1.9 2015/10/28 09:20:15 shm Exp $	*/
 
 /*	$eterna: main.c,v 1.6 2011/11/18 09:21:15 mrg Exp $	*/
 /* from: eterna: bozohttpd.c,v 1.159 2009/05/23 02:14:30 mrg Exp 	*/
@@ -65,12 +65,15 @@ usage(bozohttpd_t *httpd, char *progname
 	bozo_warn(httpd, "   -d\t\t\tenable debug support");
 #endif
 	bozo_warn(httpd, "   -s\t\t\talways log to stderr");
+#ifndef NO_DYNAMIC_CONTENT
+	bozo_warn(httpd, "   -M arg t c c11\tadd this mime extenstion");
+#endif
 #ifndef NO_USER_SUPPORT
 	bozo_warn(httpd, "   -u\t\t\tenable ~user/public_html support");
-	bozo_warn(httpd, "   -p dir\t\tchange `public_html' directory name]");
+	bozo_warn(httpd, "   -p dir\t\tchange `public_html' directory name");
+#ifndef NO_CGIBIN_SUPPORT
+	bozo_warn(httpd, "   -E\t\t\tenable CGI support for user dirs");
 #endif
-#ifndef NO_DYNAMIC_CONTENT
-	bozo_warn(httpd, "   -M arg t c c11\tadd this mime extenstion");
 #endif
 #ifndef NO_CGIBIN_SUPPORT
 #ifndef NO_DYNAMIC_CONTENT
@@ -98,9 +101,6 @@ usage(bozohttpd_t *httpd, char *progname
 	bozo_warn(httpd,
 		"   -v virtualroot\tenable virtual host support "
 		"in this directory");
-	bozo_warn(httpd,
-		"   -r\t\t\tmake sure sub-pages come from "
-		"this host via referrer");
 #ifndef NO_DIRINDEX_SUPPORT
 	bozo_warn(httpd,
 		"   -X\t\t\tenable automatic directory index support");
@@ -140,9 +140,13 @@ main(int argc, char **argv)
 
 	bozo_set_defaults(&httpd, &prefs);
 
+	/*
+	 * -r option was removed, do not reuse it for a while
+	 */
+
 	while ((c = getopt(argc, argv,
-	    "C:HI:L:M:P:S:U:VXZ:bc:defhi:np:rst:uv:x:z:")) != -1) {
-		switch(c) {
+	    "C:EHI:L:M:P:S:U:VXZ:bc:defhi:np:st:uv:x:z:")) != -1) {
+		switch (c) {
 
 		case 'L':
 #ifdef NO_LUA_SUPPORT
@@ -176,10 +180,6 @@ main(int argc, char **argv)
 			bozo_set_pref(&prefs, "numeric", "true");
 			break;
 
-		case 'r':
-			bozo_set_pref(&prefs, "trusted referal", "true");
-			break;
-
 		case 's':
 			bozo_set_pref(&prefs, "log to stderr", "true");
 			break;
@@ -297,6 +297,7 @@ main(int argc, char **argv)
 #ifdef NO_USER_SUPPORT
 		case 'p':
 		case 'u':
+		case 'E':
 			bozo_err(&httpd, 1, "User support is not enabled");
 			/* NOTREACHED */
 #else
@@ -307,6 +308,15 @@ main(int argc, char **argv)
 		case 'u':
 			bozo_set_pref(&prefs, "enable users", "true");
 			break;
+#ifndef NO_CGIBIN_SUPPORT
+		case 'E':
+			bozo_set_pref(&prefs, "enable user cgibin", "true");
+			break;
+#else
+		case 'E':
+			bozo_err(&httpd, 1, "CGI is not enabled");
+			/* NOTREACHED */
+#endif /* NO_CGIBIN_SPPORT */
 #endif /* NO_USER_SUPPORT */
 
 #ifdef NO_DIRINDEX_SUPPORT

Index: src/libexec/httpd/tilde-luzah-bozo.c
diff -u src/libexec/httpd/tilde-luzah-bozo.c:1.11 src/libexec/httpd/tilde-luzah-bozo.c:1.12
--- src/libexec/httpd/tilde-luzah-bozo.c:1.11	Thu Jul 16 12:19:23 2015
+++ src/libexec/httpd/tilde-luzah-bozo.c	Wed Oct 28 09:20:15 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: tilde-luzah-bozo.c,v 1.11 2015/07/16 12:19:23 shm Exp $	*/
+/*	$NetBSD: tilde-luzah-bozo.c,v 1.12 2015/10/28 09:20:15 shm Exp $	*/
 
 /*	$eterna: tilde-luzah-bozo.c,v 1.16 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -56,16 +56,14 @@
  * enabled.
  */
 int
-bozo_user_transform(bozo_httpreq_t *request, int *isindex)
+bozo_user_transform(bozo_httpreq_t *request)
 {
 	bozohttpd_t *httpd = request->hr_httpd;
-	char	c, *s, *file = NULL, *user;
+	char	*s, *file = NULL, *user;
 	struct	passwd *pw;
 
-	*isindex = 0;
-
 	/* find username */
-	user = strchr(request->hr_file + 2, '~');
+	user = strchr(request->hr_file + 1, '~');
 
 	/* this shouldn't happen, but "better paranoid than sorry" */
 	assert(user != NULL);
@@ -74,17 +72,24 @@ bozo_user_transform(bozo_httpreq_t *requ
 
 	if ((s = strchr(user, '/')) != NULL) {
 		*s++ = '\0';
-		c = s[strlen(s)-1];
-		*isindex = (c == '/' || c == '\0');
 	}
 
 	debug((httpd, DEBUG_OBESE, "looking for user %s",
 		user));
 	pw = getpwnam(user);
+	request->hr_user = bozostrdup(httpd, user);
+
 	/* fix this up immediately */
-	if (s)
+	if (s) {
 		s[-1] = '/';
+		/* omit additional slashes at the beginning */
+		while (*s == '/')
+			s++;
+	}
+
 	if (pw == NULL) {
+		free(request->hr_user);
+		request->hr_user = NULL;
 		(void)bozo_http_error(httpd, 404, request, "no such user");
 		return 0;
 	}
@@ -108,26 +113,11 @@ bozo_user_transform(bozo_httpreq_t *requ
 		return 0;
 	}
 	if (s == NULL || *s == '\0') {
-		file = bozostrdup(httpd, httpd->index_html);
+		file = bozostrdup(httpd, "/");
 	} else {
-		file = bozomalloc(httpd, strlen(s) +
-		    (*isindex ? strlen(httpd->index_html) + 1 : 1));
-		strcpy(file, s);
-		if (*isindex)
-			strcat(file, httpd->index_html);
-	}
-
-	/* see transform_request() */
-	if (*file == '/' || strcmp(file, "..") == 0 ||
-	    strstr(file, "/..") || strstr(file, "../")) {
-		(void)bozo_http_error(httpd, 403, request, "illegal request");
-		free(file);
-		return 0;
-	}
-
-	if (bozo_auth_check(request, file)) {
-		free(file);
-		return 0;
+		file = bozomalloc(httpd, strlen(s) + 2);
+		strcpy(file, "/");
+		strcat(file, s);
 	}
 
 	free(request->hr_file);

Index: src/libexec/httpd/lua/bozo.lua
diff -u src/libexec/httpd/lua/bozo.lua:1.1.1.1 src/libexec/httpd/lua/bozo.lua:1.2
--- src/libexec/httpd/lua/bozo.lua:1.1.1.1	Mon May 10 03:30:04 2010
+++ src/libexec/httpd/lua/bozo.lua	Wed Oct 28 09:20:15 2015
@@ -35,6 +35,7 @@ dofile "optparse.lua"
 opt = OptionParser{usage="%prog [options] root [vhost]", version="20091105"}                           
 
 opt.add_option{"-C", "--cgimap", action="store", dest="cgimap", help="--cgimap 's t'"}
+opt.add_option{"-E", "--enable-user-cgibin", action="store_true", dest="enableusercgibin", help="--enable-user-cgibin"}
 opt.add_option{"-H", "--hide-dots", action="store_true", dest="hidedots", help="--hide-dots"}
 opt.add_option{"-I", "--portnum", action="store", dest="portnum", help="--portnum number"}
 opt.add_option{"-M", "--dynamicmime", action="store", dest="dynmime", help="--dynamicmime 'suffix type a b'"}
@@ -50,7 +51,6 @@ opt.add_option{"-f", "--foreground", act
 opt.add_option{"-i", "--bindaddr", action="store", dest="bindaddress", help="--bindaddr address"}
 opt.add_option{"-n", "--numeric", action="store_true", dest="numeric", help="--numeric"}
 opt.add_option{"-p", "--public-html", action="store", dest="public_html", help="--public-html dir"}
-opt.add_option{"-r", "--trusted-referal", action="store_true", dest="trustedref", help="trusted referal"}
 opt.add_option{"-s", "--logtostderr", action="store_true", dest="logstderr", help="log to stderr"}
 opt.add_option{"-t", "--chroot", action="store", dest="chroot", help="--chroot dir"}
 opt.add_option{"-u", "--enable-users", action="store_true", dest="enableusers", help="--enable-users"}
@@ -141,6 +141,9 @@ end
 if options.hidedots then
         bozohttpd.set_pref(prefs, "hide dots", "true")
 end
+if options.enableusercgibin then
+        bozohttpd.set_pref(prefs, "enable user cgibin", "true")
+end
 if options.dirindex then
         bozohttpd.set_pref(prefs, "directory indexing", "true")
 end

Reply via email to