kore

a fork of the worlds most advanced web framework
Log | Files | Refs | README | LICENSE

commit 1447f6573f8272adf950f6a44f8deb036e3a19aa
parent 3312a2882f7f1eea041c8823225e7f4a8c810d03
Author: Joris Vink <joris@coders.se>
Date:   Tue, 17 Jul 2018 20:17:05 +0200

better http header validation.

Diffstat:
Msrc/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); +}