nghttpd: Check disallowed headers

This commit is contained in:
Tatsuhiro Tsujikawa 2013-08-28 03:14:19 +09:00
parent 3544bfdbef
commit 400615ca35
2 changed files with 40 additions and 33 deletions

View File

@ -45,6 +45,7 @@
#include <event2/listener.h> #include <event2/listener.h>
#include "app_helper.h" #include "app_helper.h"
#include "http2.h"
#include "util.h" #include "util.h"
#ifndef O_BINARY #ifndef O_BINARY
@ -608,39 +609,26 @@ void prepare_status_response(Request *req, Http2Handler *hd,
namespace { namespace {
void prepare_response(Request *req, Http2Handler *hd) void prepare_response(Request *req, Http2Handler *hd)
{ {
std::string url; auto url = (*std::lower_bound(std::begin(req->headers),
bool url_found = false; std::end(req->headers),
bool method_found = false; std::make_pair(std::string(":path"),
bool scheme_found = false; std::string()))).second;
bool host_found = false; auto ims = std::lower_bound(std::begin(req->headers),
std::end(req->headers),
std::make_pair(std::string("if-modified-since"),
std::string()));
time_t last_mod = 0; time_t last_mod = 0;
bool last_mod_found = false; bool last_mod_found = false;
for(int i = 0; i < (int)req->headers.size(); ++i) { if(ims != std::end(req->headers) &&
const std::string &field = req->headers[i].first; (*ims).first == "if-modified-since") {
const std::string &value = req->headers[i].second;
if(!url_found && field == ":path") {
url_found = true;
url = value;
} else if(field == ":method") {
method_found = true;
} else if(field == ":scheme") {
scheme_found = true;
} else if(field == ":host") {
host_found = true;
} else if(!last_mod_found && field == "if-modified-since") {
last_mod_found = true; last_mod_found = true;
last_mod = util::parse_http_date(value); last_mod = util::parse_http_date((*ims).second);
}
}
if(!url_found || !method_found || !scheme_found || !host_found) {
prepare_status_response(req, hd, STATUS_400);
return;
} }
auto query_pos = url.find("?"); auto query_pos = url.find("?");
if(query_pos != std::string::npos) { if(query_pos != std::string::npos) {
// Do not response to this request to allow clients to test timeouts. // Do not response to this request to allow clients to test timeouts.
if (url.find("nghttpd_do_not_respond_to_req=yes", if(url.find("nghttpd_do_not_respond_to_req=yes",
query_pos) != std::string::npos) { query_pos) != std::string::npos) {
return; return;
} }
url = url.substr(0, query_pos); url = url.substr(0, query_pos);
@ -690,6 +678,12 @@ void append_nv(Request *req, nghttp2_nv *nva, size_t nvlen)
} }
} // namespace } // namespace
namespace {
const char *REQUIRED_HEADERS[] = {
":host", ":method", ":path", ":scheme", nullptr
};
} // namespace
namespace { namespace {
void hd_on_frame_recv_callback void hd_on_frame_recv_callback
(nghttp2_session *session, nghttp2_frame *frame, void *user_data) (nghttp2_session *session, nghttp2_frame *frame, void *user_data)
@ -704,15 +698,25 @@ void hd_on_frame_recv_callback
switch(frame->headers.cat) { switch(frame->headers.cat) {
case NGHTTP2_HCAT_REQUEST: { case NGHTTP2_HCAT_REQUEST: {
int32_t stream_id = frame->hd.stream_id; int32_t stream_id = frame->hd.stream_id;
if(!http2::check_http2_headers(frame->headers.nva,
frame->headers.nvlen)) {
nghttp2_submit_rst_stream(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
return;
}
for(size_t i = 0; REQUIRED_HEADERS[i]; ++i) {
if(!http2::get_unique_header(frame->headers.nva,
frame->headers.nvlen,
REQUIRED_HEADERS[i])) {
nghttp2_submit_rst_stream(session, stream_id,
NGHTTP2_PROTOCOL_ERROR);
return;
}
}
auto req = util::make_unique<Request>(stream_id); auto req = util::make_unique<Request>(stream_id);
append_nv(req.get(), frame->headers.nva, frame->headers.nvlen); append_nv(req.get(), frame->headers.nva, frame->headers.nvlen);
hd->add_stream(stream_id, std::move(req)); hd->add_stream(stream_id, std::move(req));
break; break;
} }
case NGHTTP2_HCAT_HEADERS:
append_nv(hd->get_stream(frame->hd.stream_id),
frame->headers.nva, frame->headers.nvlen);
break;
default: default:
break; break;
} }
@ -727,7 +731,10 @@ void htdocs_on_request_recv_callback
(nghttp2_session *session, int32_t stream_id, void *user_data) (nghttp2_session *session, int32_t stream_id, void *user_data)
{ {
auto hd = reinterpret_cast<Http2Handler*>(user_data); auto hd = reinterpret_cast<Http2Handler*>(user_data);
prepare_response(hd->get_stream(stream_id), hd); auto stream = hd->get_stream(stream_id);
if(stream) {
prepare_response(hd->get_stream(stream_id), hd);
}
} }
namespace { namespace {

View File

@ -44,8 +44,8 @@ LDADD = $(top_builddir)/lib/libnghttp2.la
bin_PROGRAMS += nghttp nghttpd nghttpx bin_PROGRAMS += nghttp nghttpd nghttpx
HELPER_OBJECTS = util.cc timegm.c app_helper.cc HELPER_OBJECTS = util.cc http2.cc timegm.c app_helper.cc
HELPER_HFILES = util.h timegm.h app_helper.h nghttp2_config.h HELPER_HFILES = util.h http2.h timegm.h app_helper.h nghttp2_config.h
HTML_PARSER_OBJECTS = HTML_PARSER_OBJECTS =
HTML_PARSER_HFILES = HtmlParser.h HTML_PARSER_HFILES = HtmlParser.h