better http header validation.

This commit is contained in:
Joris Vink 2018-07-17 20:17:05 +02:00
parent 3312a2882f
commit 1447f6573f
1 changed files with 108 additions and 15 deletions

View File

@ -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);
}