Module Name:    src
Committed By:   mrg
Date:           Fri Apr 15 17:57:21 UTC 2016

Modified Files:
        src/libexec/httpd: CHANGES bozohttpd.8 bozohttpd.c bozohttpd.h
            cgi-bozo.c

Log Message:
updates and bozohttpd 20160415:
o  add search-word support for CGI
o  fix a security issue in CGI suffix handler support which would
   allow remote code execution, from s...@netbsd.org
o  -C option supports now CGI scripts only


To generate a diff of this commit:
cvs rdiff -u -r1.21 -r1.22 src/libexec/httpd/CHANGES
cvs rdiff -u -r1.58 -r1.59 src/libexec/httpd/bozohttpd.8
cvs rdiff -u -r1.79 -r1.80 src/libexec/httpd/bozohttpd.c
cvs rdiff -u -r1.44 -r1.45 src/libexec/httpd/bozohttpd.h
cvs rdiff -u -r1.32 -r1.33 src/libexec/httpd/cgi-bozo.c

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.21 src/libexec/httpd/CHANGES:1.22
--- src/libexec/httpd/CHANGES:1.21	Wed Oct 28 09:20:15 2015
+++ src/libexec/httpd/CHANGES	Fri Apr 15 17:57:21 2016
@@ -1,5 +1,11 @@
 $eterna: CHANGES,v 1.78 2011/11/18 01:25:11 mrg Exp $
 
+changes in bozohttpd 20160415:
+	o  add search-word support for CGI
+	o  fix a security issue in CGI suffix handler support which would
+	   allow remote code execution, from s...@netbsd.org
+	o  -C option supports now CGI scripts only
+
 changes in bozohttpd 20151028:
 	o  add CGI support for ~user translation (-E switch)
 	o  add redirects to ~user translation

Index: src/libexec/httpd/bozohttpd.8
diff -u src/libexec/httpd/bozohttpd.8:1.58 src/libexec/httpd/bozohttpd.8:1.59
--- src/libexec/httpd/bozohttpd.8:1.58	Sun Dec 27 10:21:35 2015
+++ src/libexec/httpd/bozohttpd.8	Fri Apr 15 17:57:21 2016
@@ -1,4 +1,4 @@
-.\"	$NetBSD: bozohttpd.8,v 1.58 2015/12/27 10:21:35 mrg Exp $
+.\"	$NetBSD: bozohttpd.8,v 1.59 2016/04/15 17:57:21 mrg Exp $
 .\"
 .\"	$eterna: bozohttpd.8,v 1.101 2011/11/18 01:25:11 mrg Exp $
 .\"
@@ -507,7 +507,7 @@ with PHP, one must use the
 option to specify a CGI handler for a particular file type.
 Typically this will be like:
 .Bd -literal
-httpd -C .php /usr/pkg/bin/php /var/www
+httpd -C .php /usr/pkg/bin/php-cgi /var/www
 .Ed
 .Sh SEE ALSO
 .Xr inetd.conf 5 ,
@@ -615,7 +615,8 @@ provided many fixes and enhancements for
 .Aq Mt s...@netbsd.org
 fixed memory leaks, various issues with userdir support,
 information disclosure issues, added support for using CGI handlers
-with directory indexing and provided various other fixes.
+with directory indexing, found several security issues and provided
+various other fixes.
 .It
 .An Arnaud Lacombe
 .Aq Mt a...@netbsd.org

Index: src/libexec/httpd/bozohttpd.c
diff -u src/libexec/httpd/bozohttpd.c:1.79 src/libexec/httpd/bozohttpd.c:1.80
--- src/libexec/httpd/bozohttpd.c:1.79	Sat Jan  2 20:35:59 2016
+++ src/libexec/httpd/bozohttpd.c	Fri Apr 15 17:57:21 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: bozohttpd.c,v 1.79 2016/01/02 20:35:59 elric Exp $	*/
+/*	$NetBSD: bozohttpd.c,v 1.80 2016/04/15 17:57:21 mrg Exp $	*/
 
 /*	$eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -109,7 +109,7 @@
 #define INDEX_HTML		"index.html"
 #endif
 #ifndef SERVER_SOFTWARE
-#define SERVER_SOFTWARE		"bozohttpd/20151231"
+#define SERVER_SOFTWARE		"bozohttpd/20160415"
 #endif
 #ifndef DIRECT_ACCESS_FILE
 #define DIRECT_ACCESS_FILE	".bzdirect"
@@ -1288,19 +1288,17 @@ check_bzredirect(bozo_httpreq_t *request
 }
 
 /* this fixes the %HH hack that RFC2396 requires.  */
-static int
-fix_url_percent(bozo_httpreq_t *request)
+int
+bozo_decode_url_percent(bozo_httpreq_t *request, char *str)
 {
 	bozohttpd_t *httpd = request->hr_httpd;
-	char	*s, *t, buf[3], *url;
+	char	*s, *t, buf[3];
 	char	*end;	/* if end is not-zero, we don't translate beyond that */
 
-	url = request->hr_file;
-
-	end = url + strlen(url);
+	end = str + strlen(str);
 
 	/* fast forward to the first % */
-	if ((s = strchr(url, '%')) == NULL)
+	if ((s = strchr(str, '%')) == NULL)
 		return 0;
 
 	t = s;
@@ -1352,7 +1350,7 @@ fix_url_percent(bozo_httpreq_t *request)
 	} while (*s);
 	*t = '\0';
 
-	debug((httpd, DEBUG_FAT, "fix_url_percent returns %s in url",
+	debug((httpd, DEBUG_FAT, "bozo_decode_url_percent returns `%s'",
 			request->hr_file));
 
 	return 0;
@@ -1383,7 +1381,7 @@ transform_request(bozo_httpreq_t *reques
 	file = NULL;
 	*isindex = 0;
 	debug((httpd, DEBUG_FAT, "tf_req: file %s", request->hr_file));
-	if (fix_url_percent(request)) {
+	if (bozo_decode_url_percent(request, request->hr_file)) {
 		goto bad_done;
 	}
 	if (check_virtual(request)) {

Index: src/libexec/httpd/bozohttpd.h
diff -u src/libexec/httpd/bozohttpd.h:1.44 src/libexec/httpd/bozohttpd.h:1.45
--- src/libexec/httpd/bozohttpd.h:1.44	Sat Jan  2 18:40:13 2016
+++ src/libexec/httpd/bozohttpd.h	Fri Apr 15 17:57:21 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: bozohttpd.h,v 1.44 2016/01/02 18:40:13 elric Exp $	*/
+/*	$NetBSD: bozohttpd.h,v 1.45 2016/04/15 17:57:21 mrg Exp $	*/
 
 /*	$eterna: bozohttpd.h,v 1.39 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -227,6 +227,7 @@ void	bozo_print_header(bozo_httpreq_t *,
 			  const char *);
 char	*bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url, int absolute);
 char	*bozo_escape_html(bozohttpd_t *httpd, const char *url);
+int	bozo_decode_url_percent(bozo_httpreq_t *, char *);
 
 /* these are similar to libc functions, no underscore here */
 void	bozowarn(bozohttpd_t *, const char *, ...)

Index: src/libexec/httpd/cgi-bozo.c
diff -u src/libexec/httpd/cgi-bozo.c:1.32 src/libexec/httpd/cgi-bozo.c:1.33
--- src/libexec/httpd/cgi-bozo.c:1.32	Thu Dec 31 04:39:16 2015
+++ src/libexec/httpd/cgi-bozo.c	Fri Apr 15 17:57:21 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: cgi-bozo.c,v 1.32 2015/12/31 04:39:16 mrg Exp $	*/
+/*	$NetBSD: cgi-bozo.c,v 1.33 2016/04/15 17:57:21 mrg Exp $	*/
 
 /*	$eterna: cgi-bozo.c,v 1.40 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -212,6 +212,136 @@ append_index_html(bozohttpd_t *httpd, ch
 		"append_index_html: url adjusted to `%s'", *url));
 }
 
+/* This function parse search-string according to section 4.4 of RFC3875 */
+static char **
+parse_search_string(bozo_httpreq_t *request, const char *query, size_t *args_len)
+{
+	struct	bozohttpd_t *httpd = request->hr_httpd;
+	size_t i;
+	char *s, *str, **args;
+	
+	*args_len = 0;
+
+	/* URI MUST not contain any unencoded '=' - RFC3875, section 4.4 */
+	if (strchr(query, '=')) {
+		return NULL;
+	}
+
+	str = bozostrdup(httpd, request, query);
+
+	/*
+	 * there's no more arguments than '+' chars in the query string as it's
+	 * the separator
+	 */
+	*args_len = 1;
+	/* count '+' in str */
+	for (s = str; (s = strchr(s, '+')); (*args_len)++);
+	
+	args = bozomalloc(httpd, sizeof(*args) * (*args_len + 1));
+ 
+	args[0] = str;
+	args[*args_len] = NULL;
+	for (s = str, i = 0; (s = strchr(s, '+'));) {
+		*s = '\0';
+		s++;
+		args[i++] = s;
+	}
+
+	/*
+	 * check if search-strings are valid:
+	 *
+	 * RFC3875, section 4.4:
+	 *
+	 * search-string = search-word *( "+" search-word )
+	 * search-word   = 1*schar
+	 * schar		 = unreserved | escaped | xreserved
+	 * xreserved	 = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "," |
+	 *		   "$"
+	 * 
+	 * section 2.3:
+	 *
+	 * hex	      = digit | "A" | "B" | "C" | "D" | "E" | "F" | "a" |
+	 *		"b" | "c" | "d" | "e" | "f"
+	 * escaped    = "%" hex hex
+	 * unreserved = alpha | digit | mark
+	 * mark	      = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+	 *
+	 * section 2.2:
+	 *
+	 * alpha	= lowalpha | hialpha
+	 * lowalpha	= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
+	 *		  "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
+	 *		  "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
+	 *		  "y" | "z"
+	 * hialpha	= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" |
+	 *		  "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" |
+	 *		  "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" |
+	 *	          "Y" | "Z"  
+	 * digit        = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+	 *		  "8" | "9"
+	 */
+#define	UNRESERVED_CHAR	"-_.!~*'()"
+#define	XRESERVED_CHAR	";/?:@&=,$"
+
+	for (i = 0; i < *args_len; i++) {
+		s = args[i];
+		/* search-word MUST have at least one schar */
+		if (*s == '\0')
+			goto parse_err;
+		while(*s) {
+			/* check if it's unreserved */
+			if (isalpha((int)*s) || isdigit((int)*s) ||
+			    strchr(UNRESERVED_CHAR, *s)) {
+				s++;
+				continue;
+			}
+
+			/* check if it's escaped */
+			if (*s == '%') {
+				if (s[1] == '\0' || s[2] == '\0')
+					goto parse_err;
+				if (!isxdigit((int)s[1]) ||
+				    !isxdigit((int)s[2]))
+					goto parse_err;
+				s += 3;
+				continue;
+			}
+
+			/* check if it's xreserved */
+
+			if (strchr(XRESERVED_CHAR, *s)) {
+				s++;
+				continue;
+			}
+
+			goto parse_err;
+		}
+	}
+
+	/* decode percent encoding */
+	for (i = 0; i < *args_len; i++) {
+		if (bozo_decode_url_percent(request, args[i]))
+			goto parse_err;
+	}
+
+	/* allocate each arg separately */
+	for (i = 0; i < *args_len; i++)
+		args[i] = bozostrdup(httpd, request, args[i]);
+	free(str);
+
+	return args;
+
+parse_err:
+
+	free (*args);
+	free (str);
+	*args = NULL;
+	*args_len = 0;
+
+	return 0;
+
+}
+
 void
 bozo_cgi_setbin(bozohttpd_t *httpd, const char *path)
 {
@@ -249,9 +379,9 @@ bozo_process_cgi(bozo_httpreq_t *request
 	bozoheaders_t *headp;
 	const char *type, *clen, *info, *cgihandler;
 	char	*query, *s, *t, *path, *env, *command, *file, *url;
-	char	**envp, **curenvp, *argv[4];
+	char	**envp, **curenvp, **argv, **search_string_argv = NULL;
 	char	*uri;
-	size_t	len;
+	size_t	i, len, search_string_argc = 0;
 	ssize_t rbytes;
 	pid_t	pid;
 	int	envpsize, ix, nph;
@@ -312,12 +442,25 @@ bozo_process_cgi(bozo_httpreq_t *request
 	} else if (len - 1 == CGIBIN_PREFIX_LEN)	/* url is "/cgi-bin/" */
 		append_index_html(httpd, &file);
 
+	/* RFC3875  sect. 4.4. - search-string support */
+	if (query != NULL) {
+		search_string_argv = parse_search_string(request, query,
+		    &search_string_argc);
+	}
+
+	debug((httpd, DEBUG_NORMAL, "parse_search_string args no: %lu",
+	    search_string_argc));
+	for (i = 0; i < search_string_argc; i++) {
+		debug((httpd, DEBUG_FAT,
+		    "search_string[%lu]: `%s'", i, search_string_argv[i]));
+	}
+
+	argv = bozomalloc(httpd, sizeof(*argv) * (3 + search_string_argc));
+
 	ix = 0;
 	if (cgihandler) {
 		command = file + 1;
 		path = bozostrdup(httpd, request, cgihandler);
-		argv[ix++] = path;
-			/* argv[] = [ path, command, query, NULL ] */
 	} else {
 		command = file + CGIBIN_PREFIX_LEN + 1;
 		if ((s = strchr(command, '/')) != NULL) {
@@ -329,12 +472,15 @@ bozo_process_cgi(bozo_httpreq_t *request
 		strcpy(path, httpd->cgibin);
 		strcat(path, "/");
 		strcat(path, command);
-			/* argv[] = [ command, query, NULL ] */
 	}
-	argv[ix++] = command;
-	argv[ix++] = query;
-	argv[ix++] = NULL;
 
+	argv[ix++] = path;
+
+	/* copy search-string args */
+	for (i = 0; i < search_string_argc; i++)
+		argv[ix++] = search_string_argv[i];
+
+	argv[ix++] = NULL;
 	nph = strncmp(command, "nph-", 4) == 0;
 
 	type = request->hr_content_type;
@@ -400,8 +546,11 @@ bozo_process_cgi(bozo_httpreq_t *request
 	bozo_setenv(httpd, "REQUEST_URI", uri, curenvp++);
 	bozo_setenv(httpd, "DATE_GMT", bozo_http_date(date, sizeof(date)),
 			curenvp++);
+	/* RFC3875 section 4.1.7 says that QUERY_STRING MUST be defined. */ 
 	if (query && *query)
 		bozo_setenv(httpd, "QUERY_STRING", query, curenvp++);
+	else
+		bozo_setenv(httpd, "QUERY_STRING", "", curenvp++);
 	if (info && *info)
 		bozo_setenv(httpd, "PATH_INFO", info, curenvp++);
 	if (type && *type)
@@ -425,8 +574,13 @@ bozo_process_cgi(bozo_httpreq_t *request
 		bozo_setenv(httpd, "REDIRECT_STATUS", "200", curenvp++);
 	bozo_auth_cgi_setenv(request, &curenvp);
 
-	debug((httpd, DEBUG_FAT, "bozo_process_cgi: going exec %s, %s %s %s",
-	    path, argv[0], strornull(argv[1]), strornull(argv[2])));
+	debug((httpd, DEBUG_FAT, "bozo_process_cgi: going exec %s with args:",
+	    path));
+
+	for (i = 0; argv[i] != NULL; i++) {
+		debug((httpd, DEBUG_FAT, "bozo_process_cgi: argv[%lu] = `%s'",
+		    i, argv[i]));
+	}
 
 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sv) == -1)
 		bozoerr(httpd, 1, "child socketpair failed: %s",
@@ -461,6 +615,9 @@ bozo_process_cgi(bozo_httpreq_t *request
 	free(query);
 	free(file);
 	free(url);
+	for (i = 0; i < search_string_argc; i++)
+		free(search_string_argv[i]);
+	free(search_string_argv);
 
 	close(sv[1]);
 
@@ -500,6 +657,10 @@ bozo_process_cgi(bozo_httpreq_t *request
 	exit(0);
 
  out:
+
+	for (i = 0; i < search_string_argc; i++)
+		free(search_string_argv[i]);
+	free(search_string_argv);
 	free(query);
 	free(file);
 	free(url);

Reply via email to