commit 1447f6573f8272adf950f6a44f8deb036e3a19aa
parent 3312a2882f7f1eea041c8823225e7f4a8c810d03
Author: Joris Vink <joris@coders.se>
Date: Tue, 17 Jul 2018 20:17:05 +0200
better http header validation.
Diffstat:
src/http.c | | | 123 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
1 file changed, 108 insertions(+), 15 deletions(-)
diff --git a/src/http.c b/src/http.c
@@ -61,6 +61,58 @@ static struct {
{ NULL, NULL },
};
+#define HTTP_MAP_LIMIT 127
+
+/*
+ * token = 1*<any CHAR except CTLs or separators>
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ */
+static const char http_token[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, '!' , 0x00, '#' , '$' , '%' , '&' , '\'',
+ 0x00, 0x00, '*' , '+' , 0x00, '-' , '.' , 0x00,
+ '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' ,
+ '8' , '9' , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, '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' , 0x00, 0x00, 0x00, '^' , '_' ,
+ '`' , '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' , 0x00, '|' , 0x00, '~' ,
+};
+
+/*
+ * field-content = <the OCTETs making up the field-value
+ * and consisting of either *TEXT or combinations
+ * of token, separators, and quoted-string>
+ */
+static const char http_field_content[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ' ' , '!' , '"' , '#' , '$' , '%' , '&' , '\'',
+ '(' , ')' , '*' , '+' , ',' , '-' , '.' , '/' ,
+ '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' ,
+ '8' , '9' , ':' , ';' , '<' , '=' , '>' , '?' ,
+ '@' , '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' , '[' , '\\', ']' , '^' , '_' ,
+ '`' , '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' , '{' , '|' , '}' , '~' ,
+};
+
static int http_body_recv(struct netbuf *);
static void http_error_response(struct connection *, int);
static void http_write_response_cookie(struct http_cookie *);
@@ -77,6 +129,7 @@ static int multipart_parse_headers(struct http_request *,
struct kore_buf *, struct kore_buf *,
const char *, const int);
+static char *http_validate_header(char *);
static struct http_request *http_request_new(struct connection *,
const char *, const char *, char *,
const char *);
@@ -584,6 +637,7 @@ http_request_cookie(struct http_request *req, const char *cookie, char **out)
int
http_header_recv(struct netbuf *nb)
{
+ struct connection *c;
size_t len;
ssize_t ret;
struct http_header *hdr;
@@ -592,10 +646,10 @@ http_header_recv(struct netbuf *nb)
u_int64_t bytes_left;
u_int8_t *end_headers;
int h, i, v, skip, l;
- char *request[4], *host, *hbuf;
- char *p, *headers[HTTP_REQ_HEADER_MAX];
- struct connection *c = (struct connection *)nb->owner;
+ char *headers[HTTP_REQ_HEADER_MAX];
+ char *value, *host, *request[4], *hbuf;
+ c = nb->owner;
kore_debug("http_header_recv(%p)", nb);
if (nb->b_len < 4)
@@ -633,19 +687,16 @@ http_header_recv(struct netbuf *nb)
if (strncasecmp(headers[i], "host", 4))
continue;
- if ((host = strchr(headers[i], ':')) == NULL) {
+ if ((host = http_validate_header(headers[i])) == NULL) {
http_error_response(c, 400);
return (KORE_RESULT_OK);
}
- *(host)++ = '\0';
-
if (*host == '\0') {
http_error_response(c, 400);
return (KORE_RESULT_OK);
}
- host++;
skip = i;
break;
}
@@ -668,18 +719,21 @@ http_header_recv(struct netbuf *nb)
if (i == skip)
continue;
- p = strchr(headers[i], ':');
- if (p == NULL) {
- kore_debug("malformed header: '%s'", headers[i]);
- continue;
+ if ((value = http_validate_header(headers[i])) == NULL) {
+ req->flags |= HTTP_REQUEST_DELETE;
+ http_error_response(c, 400);
+ return (KORE_RESULT_OK);
+ }
+
+ if (*value == '\0') {
+ req->flags |= HTTP_REQUEST_DELETE;
+ http_error_response(c, 400);
+ return (KORE_RESULT_OK);
}
- *(p++) = '\0';
- if (*p == ' ')
- p++;
hdr = kore_pool_get(&http_header_pool);
hdr->header = headers[i];
- hdr->value = p;
+ hdr->value = value;
TAILQ_INSERT_TAIL(&(req->req_headers), hdr, list);
if (req->agent == NULL &&
@@ -2083,3 +2137,42 @@ http_media_type(const char *path)
return (NULL);
}
+
+static char *
+http_validate_header(char *header)
+{
+ u_int8_t idx;
+ char *p, *value;
+
+ for (p = header; *p != '\0'; p++) {
+ idx = *p;
+ if (idx > HTTP_MAP_LIMIT)
+ return (NULL);
+
+ if (*p == ':') {
+ *(p)++ = '\0';
+ break;
+ }
+
+ if (http_token[idx] == 0x00)
+ return (NULL);
+ }
+
+ while (isspace(*(unsigned char *)p))
+ p++;
+
+ if (*p == '\0')
+ return (NULL);
+
+ value = p;
+ while (*p != '\0') {
+ idx = *p;
+ if (idx > HTTP_MAP_LIMIT)
+ return (NULL);
+ if (http_field_content[idx] == 0x00)
+ return (NULL);
+ p++;
+ }
+
+ return (value);
+}