Check incoming header field name strictly
The validation rule is described in http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-02#section-4.1.2
This commit is contained in:
parent
556212a602
commit
82b9f78fc7
|
@ -721,17 +721,13 @@ ssize_t nghttp2_frame_nv_offset(const uint8_t *head)
|
||||||
|
|
||||||
int nghttp2_frame_nv_check_null(const char **nv)
|
int nghttp2_frame_nv_check_null(const char **nv)
|
||||||
{
|
{
|
||||||
size_t i, j;
|
size_t i;
|
||||||
for(i = 0; nv[i]; i += 2) {
|
for(i = 0; nv[i]; i += 2) {
|
||||||
if(nv[i][0] == '\0' || nv[i+1] == NULL) {
|
if(nv[i+1] == NULL ||
|
||||||
|
!nghttp2_check_header_name_nocase((const uint8_t*)nv[i],
|
||||||
|
strlen(nv[i]))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
for(j = 0; nv[i][j]; ++j) {
|
|
||||||
unsigned char c = nv[i][j];
|
|
||||||
if(c < 0x20 || c > 0x7e) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -623,7 +623,8 @@ void nghttp2_nv_array_del(nghttp2_nv *nva);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks names are not empty string and do not contain control
|
* Checks names are not empty string and do not contain control
|
||||||
* characters and values are not NULL.
|
* characters and values are not NULL. This function allows captital
|
||||||
|
* alphabet letters in name.
|
||||||
*
|
*
|
||||||
* This function returns nonzero if it succeeds, or 0.
|
* This function returns nonzero if it succeeds, or 0.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -914,6 +914,10 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
rv = NGHTTP2_ERR_HEADER_COMP;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
if(!nghttp2_check_header_name(in, namelen)) {
|
||||||
|
rv = NGHTTP2_ERR_HEADER_COMP;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
nv.name = in;
|
nv.name = in;
|
||||||
in += namelen;
|
in += namelen;
|
||||||
in = decode_length(&valuelen, in, last, 8);
|
in = decode_length(&valuelen, in, last, 8);
|
||||||
|
@ -1005,6 +1009,10 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
rv = NGHTTP2_ERR_HEADER_COMP;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
if(!nghttp2_check_header_name(in, namelen)) {
|
||||||
|
rv = NGHTTP2_ERR_HEADER_COMP;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
nv.name = in;
|
nv.name = in;
|
||||||
in += namelen;
|
in += namelen;
|
||||||
in = decode_length(&subindex, in, last, 8);
|
in = decode_length(&subindex, in, last, 8);
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
*/
|
*/
|
||||||
#include "nghttp2_helper.h"
|
#include "nghttp2_helper.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "nghttp2_net.h"
|
#include "nghttp2_net.h"
|
||||||
|
@ -122,6 +123,71 @@ int nghttp2_should_send_window_update(int32_t local_window_size,
|
||||||
return recv_window_size >= local_window_size / 2;
|
return recv_window_size >= local_window_size / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int VALID_HD_NAME_CHARS[] = {
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
1 /* ! */,
|
||||||
|
-1,
|
||||||
|
1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
|
||||||
|
-1, -1,
|
||||||
|
1 /* * */, 1 /* + */,
|
||||||
|
-1,
|
||||||
|
1 /* - */, 1 /* . */,
|
||||||
|
-1,
|
||||||
|
1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */,
|
||||||
|
1 /* 6 */, 1 /* 7 */, 1 /* 8 */, 1 /* 9 */,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
1 /* ^ */, 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, 1 /* d */,
|
||||||
|
1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
|
||||||
|
1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
|
||||||
|
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, 1 /* x */, 1 /* y */,
|
||||||
|
1 /* z */,
|
||||||
|
-1,
|
||||||
|
1 /* | */,
|
||||||
|
-1,
|
||||||
|
1 /* ~ */,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||||
|
};
|
||||||
|
|
||||||
|
static int check_header_name(const uint8_t *name, size_t len, int nocase)
|
||||||
|
{
|
||||||
|
const uint8_t *last;
|
||||||
|
if(len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(*name == ':') {
|
||||||
|
if(len == 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
++name;
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
for(last = name + len; name != last; ++name) {
|
||||||
|
if(nocase && 'A' <= *name && *name <= 'Z') continue;
|
||||||
|
if(VALID_HD_NAME_CHARS[*name] == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nghttp2_check_header_name(const uint8_t *name, size_t len)
|
||||||
|
{
|
||||||
|
return check_header_name(name, len, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nghttp2_check_header_name_nocase(const uint8_t *name, size_t len)
|
||||||
|
{
|
||||||
|
return check_header_name(name, len, 1);
|
||||||
|
}
|
||||||
|
|
||||||
const char* nghttp2_strerror(int error_code)
|
const char* nghttp2_strerror(int error_code)
|
||||||
{
|
{
|
||||||
switch(error_code) {
|
switch(error_code) {
|
||||||
|
|
|
@ -116,4 +116,19 @@ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
|
||||||
int nghttp2_should_send_window_update(int32_t local_window_size,
|
int nghttp2_should_send_window_update(int32_t local_window_size,
|
||||||
int32_t recv_window_size);
|
int32_t recv_window_size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks the header name in |name| with |len| bytes is valid.
|
||||||
|
*
|
||||||
|
* This function returns nonzero if it succeeds, or 0.
|
||||||
|
*/
|
||||||
|
int nghttp2_check_header_name(const uint8_t *name, size_t len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks the header name in |name| with |len| bytes is valid. This
|
||||||
|
* function accepts also characters in [A-Z].
|
||||||
|
*
|
||||||
|
* This function returns nonzero if it succeeds, or 0.
|
||||||
|
*/
|
||||||
|
int nghttp2_check_header_name_nocase(const uint8_t *name, size_t len);
|
||||||
|
|
||||||
#endif /* NGHTTP2_HELPER_H */
|
#endif /* NGHTTP2_HELPER_H */
|
||||||
|
|
|
@ -240,7 +240,9 @@ int main(int argc, char* argv[])
|
||||||
test_nghttp2_hd_deflate_inflate) ||
|
test_nghttp2_hd_deflate_inflate) ||
|
||||||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
|
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
|
||||||
!CU_add_test(pSuite, "adjust_local_window_size",
|
!CU_add_test(pSuite, "adjust_local_window_size",
|
||||||
test_nghttp2_adjust_local_window_size)
|
test_nghttp2_adjust_local_window_size) ||
|
||||||
|
!CU_add_test(pSuite, "check_header_name",
|
||||||
|
test_nghttp2_check_header_name)
|
||||||
) {
|
) {
|
||||||
CU_cleanup_registry();
|
CU_cleanup_registry();
|
||||||
return CU_get_error();
|
return CU_get_error();
|
||||||
|
|
|
@ -85,7 +85,7 @@ void test_nghttp2_frame_nv_check_null(void)
|
||||||
const char *headers1[] = { "path", "/", "host", "a", NULL };
|
const char *headers1[] = { "path", "/", "host", "a", NULL };
|
||||||
const char *headers2[] = { "", "/", "host", "a", NULL };
|
const char *headers2[] = { "", "/", "host", "a", NULL };
|
||||||
const char *headers3[] = { "path", "/", "host\x01", "a", NULL };
|
const char *headers3[] = { "path", "/", "host\x01", "a", NULL };
|
||||||
const char *headers4[] = { "path", "/", "host", NULL, NULL };
|
const char *headers4[] = { "PATH", "/", "host", NULL, NULL };
|
||||||
|
|
||||||
CU_ASSERT(nghttp2_frame_nv_check_null(headers1));
|
CU_ASSERT(nghttp2_frame_nv_check_null(headers1));
|
||||||
CU_ASSERT(0 == nghttp2_frame_nv_check_null(headers2));
|
CU_ASSERT(0 == nghttp2_frame_nv_check_null(headers2));
|
||||||
|
|
|
@ -73,3 +73,32 @@ void test_nghttp2_adjust_local_window_size(void)
|
||||||
CU_ASSERT(100 == local_window_size);
|
CU_ASSERT(100 == local_window_size);
|
||||||
CU_ASSERT(50 == recv_window_size);
|
CU_ASSERT(50 == recv_window_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int check_header_name(const char *s)
|
||||||
|
{
|
||||||
|
return nghttp2_check_header_name((const uint8_t*)s, strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_header_name_nocase(const char *s)
|
||||||
|
{
|
||||||
|
return nghttp2_check_header_name_nocase((const uint8_t*)s, strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_check_header_name(void)
|
||||||
|
{
|
||||||
|
CU_ASSERT(check_header_name(":path"));
|
||||||
|
CU_ASSERT(check_header_name("path"));
|
||||||
|
CU_ASSERT(check_header_name("!#$%&'*+-.^_`|~"));
|
||||||
|
CU_ASSERT(!check_header_name(":PATH"));
|
||||||
|
CU_ASSERT(!check_header_name("path:"));
|
||||||
|
CU_ASSERT(!check_header_name(""));
|
||||||
|
CU_ASSERT(!check_header_name(":"));
|
||||||
|
|
||||||
|
CU_ASSERT(check_header_name_nocase(":path"));
|
||||||
|
CU_ASSERT(check_header_name_nocase("path"));
|
||||||
|
CU_ASSERT(check_header_name_nocase("!#$%&'*+-.^_`|~"));
|
||||||
|
CU_ASSERT(check_header_name_nocase(":PATH"));
|
||||||
|
CU_ASSERT(!check_header_name_nocase("path:"));
|
||||||
|
CU_ASSERT(!check_header_name_nocase(""));
|
||||||
|
CU_ASSERT(!check_header_name_nocase(":"));
|
||||||
|
}
|
||||||
|
|
|
@ -26,5 +26,6 @@
|
||||||
#define NGHTTP2_HELPER_TEST_H
|
#define NGHTTP2_HELPER_TEST_H
|
||||||
|
|
||||||
void test_nghttp2_adjust_local_window_size(void);
|
void test_nghttp2_adjust_local_window_size(void);
|
||||||
|
void test_nghttp2_check_header_name(void);
|
||||||
|
|
||||||
#endif /* NGHTTP2_HELPER_TEST_H */
|
#endif /* NGHTTP2_HELPER_TEST_H */
|
||||||
|
|
|
@ -1606,7 +1606,7 @@ void test_nghttp2_submit_response(void)
|
||||||
{
|
{
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
const char *nv[] = { "Content-Length", "1024", NULL };
|
const char *nv[] = { "content-length", "1024", NULL };
|
||||||
nghttp2_data_provider data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
my_user_data ud;
|
my_user_data ud;
|
||||||
nghttp2_outbound_item *item;
|
nghttp2_outbound_item *item;
|
||||||
|
@ -1632,7 +1632,7 @@ void test_nghttp2_submit_response_without_data(void)
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
accumulator acc;
|
accumulator acc;
|
||||||
const char *nv[] = { ":Version", "HTTP/1.1", NULL };
|
const char *nv[] = { ":version", "HTTP/1.1", NULL };
|
||||||
nghttp2_data_provider data_prd = {{-1}, NULL};
|
nghttp2_data_provider data_prd = {{-1}, NULL};
|
||||||
nghttp2_outbound_item *item;
|
nghttp2_outbound_item *item;
|
||||||
my_user_data ud;
|
my_user_data ud;
|
||||||
|
@ -1665,7 +1665,7 @@ void test_nghttp2_submit_request_with_data(void)
|
||||||
{
|
{
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
const char *nv[] = { ":Version", "HTTP/1.1", NULL };
|
const char *nv[] = { ":version", "HTTP/1.1", NULL };
|
||||||
nghttp2_data_provider data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
my_user_data ud;
|
my_user_data ud;
|
||||||
nghttp2_outbound_item *item;
|
nghttp2_outbound_item *item;
|
||||||
|
@ -1691,7 +1691,7 @@ void test_nghttp2_submit_request_without_data(void)
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
accumulator acc;
|
accumulator acc;
|
||||||
const char *nv[] = { ":Version", "HTTP/1.1", NULL };
|
const char *nv[] = { ":version", "HTTP/1.1", NULL };
|
||||||
nghttp2_data_provider data_prd = {{-1}, NULL};
|
nghttp2_data_provider data_prd = {{-1}, NULL};
|
||||||
nghttp2_outbound_item *item;
|
nghttp2_outbound_item *item;
|
||||||
my_user_data ud;
|
my_user_data ud;
|
||||||
|
@ -1842,7 +1842,7 @@ void test_nghttp2_submit_headers(void)
|
||||||
{
|
{
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
const char *nv[] = { ":Version", "HTTP/1.1", NULL };
|
const char *nv[] = { ":version", "HTTP/1.1", NULL };
|
||||||
my_user_data ud;
|
my_user_data ud;
|
||||||
nghttp2_outbound_item *item;
|
nghttp2_outbound_item *item;
|
||||||
nghttp2_stream *stream;
|
nghttp2_stream *stream;
|
||||||
|
|
Loading…
Reference in New Issue