summaryrefslogtreecommitdiff
path: root/libexec/httpd
diff options
context:
space:
mode:
authormrg <mrg@NetBSD.org>2016-04-15 17:57:21 +0000
committermrg <mrg@NetBSD.org>2016-04-15 17:57:21 +0000
commit2bba450866f3c11cf63f34097e8c35a0dd678910 (patch)
tree3c583b3e82ed12965921cd046784c1f4ae7a406c /libexec/httpd
parent448c52de04c976d3493f1ba1754a281ce03f5575 (diff)
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 shm@netbsd.org o -C option supports now CGI scripts only
Diffstat (limited to 'libexec/httpd')
-rw-r--r--libexec/httpd/CHANGES6
-rw-r--r--libexec/httpd/bozohttpd.87
-rw-r--r--libexec/httpd/bozohttpd.c20
-rw-r--r--libexec/httpd/bozohttpd.h3
-rw-r--r--libexec/httpd/cgi-bozo.c183
5 files changed, 193 insertions, 26 deletions
diff --git a/libexec/httpd/CHANGES b/libexec/httpd/CHANGES
index 2f11017e51e..7b3969fcd59 100644
--- a/libexec/httpd/CHANGES
+++ b/libexec/httpd/CHANGES
@@ -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 shm@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
diff --git a/libexec/httpd/bozohttpd.8 b/libexec/httpd/bozohttpd.8
index f9c02f83957..6e636a61c97 100644
--- a/libexec/httpd/bozohttpd.8
+++ b/libexec/httpd/bozohttpd.8
@@ -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 the man page
.Aq Mt shm@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 alc@NetBSD.org
diff --git a/libexec/httpd/bozohttpd.c b/libexec/httpd/bozohttpd.c
index 0ef800e95d6..e2f1ed55fff 100644
--- a/libexec/httpd/bozohttpd.c
+++ b/libexec/httpd/bozohttpd.c
@@ -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 *request, int *isindex)
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)) {
diff --git a/libexec/httpd/bozohttpd.h b/libexec/httpd/bozohttpd.h
index 41f271f3ca9..e53d313f498 100644
--- a/libexec/httpd/bozohttpd.h
+++ b/libexec/httpd/bozohttpd.h
@@ -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 *, struct stat *, const char *,
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 *, ...)
diff --git a/libexec/httpd/cgi-bozo.c b/libexec/httpd/cgi-bozo.c
index 613e85d1a90..a81ee706c0e 100644
--- a/libexec/httpd/cgi-bozo.c
+++ b/libexec/httpd/cgi-bozo.c
@@ -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, char **url)
"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);