Properly HTML-escape names in dirlistings
Based on a patch by guysv. We now make sure that the valid path-characters ", ', <, >, & can not be used for XSS on a target, for example with a file called "><img src="blabla" onerror="alert(1)" by properly HTML-escaping these characters. Signed-off-by: Laslo Hunhold <dev@frign.de>
This commit is contained in:
parent
5ee8c07e7e
commit
48e74a5982
1 changed files with 51 additions and 3 deletions
54
resp.c
54
resp.c
|
@ -38,6 +38,51 @@ suffix(int t)
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
html_escape(char *src, char *dst, size_t dst_siz)
|
||||||
|
{
|
||||||
|
const struct {
|
||||||
|
char c;
|
||||||
|
char *s;
|
||||||
|
} escape[] = {
|
||||||
|
{ '&', "&" },
|
||||||
|
{ '<', "<" },
|
||||||
|
{ '>', ">" },
|
||||||
|
{ '"', """ },
|
||||||
|
{ '\'', "'" },
|
||||||
|
};
|
||||||
|
size_t i, j, k, esclen;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; src[i] != '\0'; i++) {
|
||||||
|
for (k = 0; k < LEN(escape); k++) {
|
||||||
|
if (src[i] == escape[k].c) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == LEN(escape)) {
|
||||||
|
/* no escape char at src[i] */
|
||||||
|
if (j == dst_siz - 1) {
|
||||||
|
/* silent truncation */
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
dst[j++] = src[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* escape char at src[i] */
|
||||||
|
esclen = strlen(escape[k].s);
|
||||||
|
|
||||||
|
if (j >= dst_siz - esclen) {
|
||||||
|
/* silent truncation */
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
memcpy(&dst[j], escape[k].s, esclen);
|
||||||
|
j += esclen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst[j] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
enum status
|
enum status
|
||||||
resp_dir(int fd, char *name, struct request *r)
|
resp_dir(int fd, char *name, struct request *r)
|
||||||
{
|
{
|
||||||
|
@ -45,6 +90,7 @@ resp_dir(int fd, char *name, struct request *r)
|
||||||
size_t i;
|
size_t i;
|
||||||
int dirlen, s;
|
int dirlen, s;
|
||||||
static char t[TIMESTAMP_LEN];
|
static char t[TIMESTAMP_LEN];
|
||||||
|
char esc[PATH_MAX /* > NAME_MAX */ * 6]; /* strlen("&...;") <= 6 */
|
||||||
|
|
||||||
/* read directory */
|
/* read directory */
|
||||||
if ((dirlen = scandir(name, &e, NULL, compareent)) < 0) {
|
if ((dirlen = scandir(name, &e, NULL, compareent)) < 0) {
|
||||||
|
@ -65,11 +111,12 @@ resp_dir(int fd, char *name, struct request *r)
|
||||||
|
|
||||||
if (r->method == M_GET) {
|
if (r->method == M_GET) {
|
||||||
/* listing header */
|
/* listing header */
|
||||||
|
html_escape(name, esc, sizeof(esc));
|
||||||
if (dprintf(fd,
|
if (dprintf(fd,
|
||||||
"<!DOCTYPE html>\n<html>\n\t<head>"
|
"<!DOCTYPE html>\n<html>\n\t<head>"
|
||||||
"<title>Index of %s</title></head>\n"
|
"<title>Index of %s</title></head>\n"
|
||||||
"\t<body>\n\t\t<a href=\"..\">..</a>",
|
"\t<body>\n\t\t<a href=\"..\">..</a>",
|
||||||
name) < 0) {
|
esc) < 0) {
|
||||||
s = S_REQUEST_TIMEOUT;
|
s = S_REQUEST_TIMEOUT;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
@ -82,10 +129,11 @@ resp_dir(int fd, char *name, struct request *r)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* entry line */
|
/* entry line */
|
||||||
|
html_escape(e[i]->d_name, esc, sizeof(esc));
|
||||||
if (dprintf(fd, "<br />\n\t\t<a href=\"%s%s\">%s%s</a>",
|
if (dprintf(fd, "<br />\n\t\t<a href=\"%s%s\">%s%s</a>",
|
||||||
e[i]->d_name,
|
esc,
|
||||||
(e[i]->d_type == DT_DIR) ? "/" : "",
|
(e[i]->d_type == DT_DIR) ? "/" : "",
|
||||||
e[i]->d_name,
|
esc,
|
||||||
suffix(e[i]->d_type)) < 0) {
|
suffix(e[i]->d_type)) < 0) {
|
||||||
s = S_REQUEST_TIMEOUT;
|
s = S_REQUEST_TIMEOUT;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
Loading…
Reference in a new issue