nghttpd, nghttpx: Check allowed pseudo headers
This commit is contained in:
parent
9d78167297
commit
6e027ad830
|
@ -1257,7 +1257,7 @@ int hd_on_frame_recv_callback
|
|||
if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
|
||||
http2::normalize_headers(stream->headers);
|
||||
if(!http2::check_http2_headers(stream->headers)) {
|
||||
if(!http2::check_http2_request_headers(stream->headers)) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
|
80
src/http2.cc
80
src/http2.cc
|
@ -162,6 +162,31 @@ namespace {
|
|||
size_t DISALLOWED_HDLEN = sizeof(DISALLOWED_HD)/sizeof(DISALLOWED_HD[0]);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *REQUEST_PSEUDO_HD[] = {
|
||||
":authority",
|
||||
":method",
|
||||
":path",
|
||||
":scheme",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
size_t REQUEST_PSEUDO_HDLEN =
|
||||
sizeof(REQUEST_PSEUDO_HD) / sizeof(REQUEST_PSEUDO_HD[0]);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *RESPONSE_PSEUDO_HD[] = {
|
||||
":status",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
size_t RESPONSE_PSEUDO_HDLEN =
|
||||
sizeof(RESPONSE_PSEUDO_HD) / sizeof(RESPONSE_PSEUDO_HD[0]);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *IGN_HD[] = {
|
||||
"connection",
|
||||
|
@ -223,6 +248,61 @@ bool check_http2_headers(const Headers& nva)
|
|||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
template<typename InputIterator>
|
||||
bool check_pseudo_headers(const Headers& nva,
|
||||
InputIterator allowed_first,
|
||||
InputIterator allowed_last)
|
||||
{
|
||||
// strict checking for pseudo headers.
|
||||
for(auto& hd : nva) {
|
||||
auto c = hd.name.c_str()[0];
|
||||
|
||||
if(c < ':') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c > ':') {
|
||||
break;
|
||||
}
|
||||
|
||||
auto i = allowed_first;
|
||||
|
||||
for(; i != allowed_last; ++i) {
|
||||
if(hd.name == *i) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i == allowed_last) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool check_http2_request_headers(const Headers& nva)
|
||||
{
|
||||
if(!check_http2_headers(nva)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return check_pseudo_headers(nva, REQUEST_PSEUDO_HD,
|
||||
REQUEST_PSEUDO_HD + REQUEST_PSEUDO_HDLEN);
|
||||
}
|
||||
|
||||
bool check_http2_response_headers(const Headers& nva)
|
||||
{
|
||||
if(!check_http2_headers(nva)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return check_pseudo_headers(nva, RESPONSE_PSEUDO_HD,
|
||||
RESPONSE_PSEUDO_HD + RESPONSE_PSEUDO_HDLEN);
|
||||
}
|
||||
|
||||
void normalize_headers(Headers& nva)
|
||||
{
|
||||
for(auto& kv : nva) {
|
||||
|
|
10
src/http2.h
10
src/http2.h
|
@ -96,6 +96,16 @@ bool check_http2_allowed_header(const char *name);
|
|||
// contains such headers.
|
||||
bool check_http2_headers(const Headers& nva);
|
||||
|
||||
// Calls check_http2_headers() and also checks that |nva| only
|
||||
// contains pseudo headers allowed in request. Returns true if all
|
||||
// checks passed.
|
||||
bool check_http2_request_headers(const Headers& nva);
|
||||
|
||||
// Calls check_http2_headers() and also checks that |nva| only
|
||||
// contains pseudo headers allowed in response. Returns true if all
|
||||
// checks passed.
|
||||
bool check_http2_response_headers(const Headers& nva);
|
||||
|
||||
bool name_less(const Headers::value_type& lhs, const Headers::value_type& rhs);
|
||||
|
||||
void normalize_headers(Headers& nva);
|
||||
|
|
|
@ -111,6 +111,21 @@ void test_http2_check_http2_headers(void)
|
|||
{ "te2", "3" }
|
||||
};
|
||||
CU_ASSERT(http2::check_http2_headers(nva3));
|
||||
|
||||
auto nva4 = Headers{
|
||||
{ ":authority", "1" },
|
||||
{ ":method", "2" },
|
||||
{ ":path", "3" },
|
||||
{ ":scheme", "4" }
|
||||
};
|
||||
CU_ASSERT(http2::check_http2_request_headers(nva4));
|
||||
CU_ASSERT(!http2::check_http2_response_headers(nva4));
|
||||
|
||||
auto nva5 = Headers{
|
||||
{ ":status", "1" }
|
||||
};
|
||||
CU_ASSERT(!http2::check_http2_request_headers(nva5));
|
||||
CU_ASSERT(http2::check_http2_response_headers(nva5));
|
||||
}
|
||||
|
||||
void test_http2_get_unique_header(void)
|
||||
|
|
|
@ -900,7 +900,7 @@ int on_response_headers(Http2Session *http2session,
|
|||
|
||||
downstream->set_expect_final_response(false);
|
||||
|
||||
if(!http2::check_http2_headers(nva)) {
|
||||
if(!http2::check_http2_response_headers(nva)) {
|
||||
http2session->submit_rst_stream(frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
|
|
|
@ -302,7 +302,7 @@ int on_request_headers(Http2Upstream *upstream,
|
|||
http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva);
|
||||
}
|
||||
|
||||
if(!http2::check_http2_headers(nva)) {
|
||||
if(!http2::check_http2_request_headers(nva)) {
|
||||
if(upstream->error_reply(downstream, 400) != 0) {
|
||||
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue