From 62b9e4bb56d84eafb42c4b8ebb92d05ff84fcf75 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 17 Jan 2015 21:31:28 +0900 Subject: [PATCH] nghttpx: Validate received request body length against content-length --- src/shrpx_downstream.cc | 46 +++++++++++++++++++++++++++++-------- src/shrpx_downstream.h | 5 ++++ src/shrpx_http2_upstream.cc | 15 ++++++++++++ src/shrpx_spdy_upstream.cc | 10 ++++++++ 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index b00512a5..9d9c973c 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -109,10 +109,10 @@ Downstream::Downstream(Upstream *upstream, int32_t stream_id, int32_t priority) : request_buf_(upstream ? upstream->get_mcpool() : nullptr), response_buf_(upstream ? upstream->get_mcpool() : nullptr), request_bodylen_(0), response_bodylen_(0), response_sent_bodylen_(0), - response_content_length_(-1), upstream_(upstream), - request_headers_sum_(0), response_headers_sum_(0), request_datalen_(0), - response_datalen_(0), stream_id_(stream_id), priority_(priority), - downstream_stream_id_(-1), + request_content_length_(-1), response_content_length_(-1), + upstream_(upstream), request_headers_sum_(0), response_headers_sum_(0), + request_datalen_(0), response_datalen_(0), stream_id_(stream_id), + priority_(priority), downstream_stream_id_(-1), response_rst_stream_error_code_(NGHTTP2_NO_ERROR), request_state_(INITIAL), request_major_(1), request_minor_(1), response_state_(INITIAL), response_http_status_(0), response_major_(1), @@ -668,18 +668,38 @@ void Downstream::set_response_content_length(int64_t len) { response_content_length_ = len; } +bool Downstream::validate_request_bodylen() const { + if (request_content_length_ == -1) { + return true; + } + + if (request_content_length_ != request_bodylen_) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "request invalid bodylen: content-length=" + << request_content_length_ + << ", received=" << request_bodylen_; + } + return false; + } + + return true; +} + bool Downstream::validate_response_bodylen() const { if (!expect_response_body() || response_content_length_ == -1) { return true; } - if (LOG_ENABLED(INFO)) { - DLOG(INFO, this) << "response invalid bodylen: content-length=" - << response_content_length_ - << ", received=" << response_bodylen_; + if (response_content_length_ != response_bodylen_) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "response invalid bodylen: content-length=" + << response_content_length_ + << ", received=" << response_bodylen_; + } + return false; } - return response_content_length_ == response_bodylen_; + return true; } void Downstream::set_priority(int32_t pri) { priority_ = pri; } @@ -705,6 +725,14 @@ void Downstream::inspect_http2_request() { if (request_method_ == "CONNECT") { upgrade_request_ = true; } + + auto idx = request_hdidx_[http2::HD_CONTENT_LENGTH]; + if (idx != -1) { + auto len = util::parse_uint(request_headers_[idx].value); + if (len != -1) { + request_content_length_ = len; + } + } } void Downstream::inspect_http1_request() { diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h index a976a6eb..7fbeb372 100644 --- a/src/shrpx_downstream.h +++ b/src/shrpx_downstream.h @@ -158,6 +158,9 @@ public: size_t get_request_datalen() const; void dec_request_datalen(size_t len); void reset_request_datalen(); + // Validates that received request body length and content-length + // matches. + bool validate_request_bodylen() const; bool request_pseudo_header_allowed(int token) const; bool expect_response_body() const; enum { @@ -309,6 +312,8 @@ private: // the length of response body sent to upstream client int64_t response_sent_bodylen_; + // content-length of request body, -1 if it is unknown. + int64_t request_content_length_; // content-length of response body, -1 if it is unknown. int64_t response_content_length_; diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 2c6ee311..61a8314c 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -302,6 +302,11 @@ int on_request_headers(Http2Upstream *upstream, Downstream *downstream, downstream->set_request_state(Downstream::HEADER_COMPLETE); if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + if (!downstream->validate_request_bodylen()) { + upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); + return 0; + } + downstream->disable_upstream_rtimer(); downstream->set_request_state(Downstream::MSG_COMPLETE); @@ -381,6 +386,11 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { downstream->disable_upstream_rtimer(); + if (!downstream->validate_request_bodylen()) { + upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); + return 0; + } + downstream->end_upload_data(); downstream->set_request_state(Downstream::MSG_COMPLETE); } @@ -400,6 +410,11 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + if (!downstream->validate_request_bodylen()) { + upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); + return 0; + } + downstream->disable_upstream_rtimer(); downstream->end_upload_data(); diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 313d6a52..92f32b98 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -204,6 +204,11 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, downstream->set_request_state(Downstream::HEADER_COMPLETE); if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { + if (!downstream->validate_request_bodylen()) { + upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); + return; + } + downstream->disable_upstream_rtimer(); downstream->set_request_state(Downstream::MSG_COMPLETE); } @@ -321,6 +326,11 @@ void on_data_recv_callback(spdylay_session *session, uint8_t flags, auto upstream = static_cast(user_data); auto downstream = upstream->find_downstream(stream_id); if (downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) { + if (!downstream->validate_request_bodylen()) { + upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); + return; + } + downstream->disable_upstream_rtimer(); downstream->end_upload_data(); downstream->set_request_state(Downstream::MSG_COMPLETE);