Allow always max 1024 padding for HEADERS
We need paddings regardless of payload and frame boundary to mitigate certain attacks. Since we handles CONTINUATION internally, we don't show FLAG_PAD_HIGH and PAD_LOW flags of HEADERS in nghttp/nghttpd. We just show the total paddings in HEADERS + CONTINUATION.
This commit is contained in:
parent
622f783675
commit
1e95c8b313
|
@ -1141,10 +1141,7 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session,
|
||||||
return framebuflen;
|
return framebuflen;
|
||||||
}
|
}
|
||||||
padded_payloadlen = session_call_select_padding
|
padded_payloadlen = session_call_select_padding
|
||||||
(session, frame,
|
(session, frame, frame->hd.length + 1024);
|
||||||
(frame->hd.length == 0 ? NGHTTP2_MAX_FRAME_LENGTH :
|
|
||||||
(frame->hd.length + NGHTTP2_MAX_FRAME_LENGTH - 1)
|
|
||||||
/ NGHTTP2_MAX_FRAME_LENGTH * NGHTTP2_MAX_FRAME_LENGTH));
|
|
||||||
if(nghttp2_is_fatal(padded_payloadlen)) {
|
if(nghttp2_is_fatal(padded_payloadlen)) {
|
||||||
return padded_payloadlen;
|
return padded_payloadlen;
|
||||||
}
|
}
|
||||||
|
@ -1152,13 +1149,46 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session,
|
||||||
frame->headers.padlen = padded_payloadlen - frame->hd.length;
|
frame->headers.padlen = padded_payloadlen - frame->hd.length;
|
||||||
frame->hd.length = padded_payloadlen;
|
frame->hd.length = padded_payloadlen;
|
||||||
|
|
||||||
|
DEBUGF(fprintf(stderr, "payloadlen=%zu, padlen=%zu\n",
|
||||||
|
frame->hd.length, frame->headers.padlen));
|
||||||
|
|
||||||
if(frame->hd.length > NGHTTP2_MAX_FRAME_LENGTH) {
|
if(frame->hd.length > NGHTTP2_MAX_FRAME_LENGTH) {
|
||||||
|
if(NGHTTP2_MAX_FRAME_LENGTH >
|
||||||
|
frame->hd.length - frame->headers.padlen) {
|
||||||
|
size_t padlen = NGHTTP2_MAX_FRAME_LENGTH -
|
||||||
|
(frame->hd.length - frame->headers.padlen);
|
||||||
|
nghttp2_frame_hd hd = frame->hd;
|
||||||
|
|
||||||
|
DEBUGF(fprintf(stderr, "padding across 2 frames\n"));
|
||||||
|
|
||||||
|
hd.flags &= ~NGHTTP2_FLAG_END_HEADERS;
|
||||||
|
hd.length = NGHTTP2_MAX_FRAME_LENGTH;
|
||||||
|
|
||||||
|
DEBUGF(fprintf(stderr, "first HEADERS payloadlen=%zu, padlen=%zu\n",
|
||||||
|
hd.length, padlen));
|
||||||
|
|
||||||
|
r = nghttp2_frame_add_pad(&session->aob.framebuf,
|
||||||
|
&session->aob.framebufmax,
|
||||||
|
&session->aob.framebufoff,
|
||||||
|
&hd.flags,
|
||||||
|
hd.length - padlen,
|
||||||
|
padlen);
|
||||||
|
if(nghttp2_is_fatal(r)) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
framebuflen = session->aob.framebufoff + frame->hd.length
|
||||||
|
+ NGHTTP2_FRAME_HEAD_LENGTH;
|
||||||
|
|
||||||
|
nghttp2_frame_pack_frame_hd
|
||||||
|
(session->aob.framebuf + session->aob.framebufoff, &hd);
|
||||||
|
} else {
|
||||||
/* PAD_HIGH and PAD_LOW will be added in
|
/* PAD_HIGH and PAD_LOW will be added in
|
||||||
nghttp2_session_after_frame_sent(). */
|
nghttp2_session_after_frame_sent(). */
|
||||||
/* This may make framebuflen > session->aob.framebufmax. But
|
framebuflen += frame->headers.padlen;
|
||||||
|
}
|
||||||
|
/* At this point, framebuflen > session->aob.framebufmax. But
|
||||||
before we access the missing part, we will allocate it in
|
before we access the missing part, we will allocate it in
|
||||||
nghttp2_session_after_frame_sent(). */
|
nghttp2_session_after_frame_sent(). */
|
||||||
framebuflen += frame->headers.padlen;
|
|
||||||
} else if(frame->hd.length <= NGHTTP2_MAX_FRAME_LENGTH &&
|
} else if(frame->hd.length <= NGHTTP2_MAX_FRAME_LENGTH &&
|
||||||
frame->headers.padlen > 0) {
|
frame->headers.padlen > 0) {
|
||||||
r = nghttp2_frame_add_pad(&session->aob.framebuf,
|
r = nghttp2_frame_add_pad(&session->aob.framebuf,
|
||||||
|
@ -1504,17 +1534,24 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
|
||||||
session->aob.framebufoff -= NGHTTP2_FRAME_HEAD_LENGTH;
|
session->aob.framebufoff -= NGHTTP2_FRAME_HEAD_LENGTH;
|
||||||
if(cont_hd.length + session->aob.framebufmark ==
|
if(cont_hd.length + session->aob.framebufmark ==
|
||||||
session->aob.framebuflen) {
|
session->aob.framebuflen) {
|
||||||
|
size_t padlen;
|
||||||
|
if(cont_hd.length < frame->headers.padlen) {
|
||||||
|
padlen = cont_hd.length;
|
||||||
|
} else {
|
||||||
|
padlen = frame->headers.padlen;
|
||||||
|
}
|
||||||
DEBUGF(fprintf(stderr,
|
DEBUGF(fprintf(stderr,
|
||||||
"last CONTINUATION payloadlen=%zu, padlen=%zu\n",
|
"last CONTINUATION payloadlen=%zu, padlen=%zu\n",
|
||||||
cont_hd.length, frame->headers.padlen));
|
cont_hd.length, padlen));
|
||||||
|
|
||||||
cont_hd.flags = NGHTTP2_FLAG_END_HEADERS;
|
cont_hd.flags = NGHTTP2_FLAG_END_HEADERS;
|
||||||
|
|
||||||
rv = nghttp2_frame_add_pad(&session->aob.framebuf,
|
rv = nghttp2_frame_add_pad(&session->aob.framebuf,
|
||||||
&session->aob.framebufmax,
|
&session->aob.framebufmax,
|
||||||
&session->aob.framebufoff,
|
&session->aob.framebufoff,
|
||||||
&cont_hd.flags,
|
&cont_hd.flags,
|
||||||
cont_hd.length - frame->headers.padlen,
|
cont_hd.length - padlen,
|
||||||
frame->headers.padlen);
|
padlen);
|
||||||
if(nghttp2_is_fatal(rv)) {
|
if(nghttp2_is_fatal(rv)) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
@ -1524,9 +1561,6 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
|
||||||
session->aob.framebuflen = session->aob.framebufmark =
|
session->aob.framebuflen = session->aob.framebufmark =
|
||||||
session->aob.framebufoff + NGHTTP2_FRAME_HEAD_LENGTH +
|
session->aob.framebufoff + NGHTTP2_FRAME_HEAD_LENGTH +
|
||||||
cont_hd.length;
|
cont_hd.length;
|
||||||
/* Or-ing flags so that we can show these flags in
|
|
||||||
callback */
|
|
||||||
frame->hd.flags |= cont_hd.flags;
|
|
||||||
} else {
|
} else {
|
||||||
cont_hd.flags = NGHTTP2_FLAG_NONE;
|
cont_hd.flags = NGHTTP2_FLAG_NONE;
|
||||||
session->aob.framebufmark += cont_hd.length;
|
session->aob.framebufmark += cont_hd.length;
|
||||||
|
@ -3506,6 +3540,8 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||||
nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->buf);
|
nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->buf);
|
||||||
iframe->payloadleft = iframe->frame.hd.length;
|
iframe->payloadleft = iframe->frame.hd.length;
|
||||||
|
|
||||||
|
DEBUGF(fprintf(stderr, "payloadlen=%zu\n", iframe->payloadleft));
|
||||||
|
|
||||||
switch(iframe->frame.hd.type) {
|
switch(iframe->frame.hd.type) {
|
||||||
case NGHTTP2_DATA: {
|
case NGHTTP2_DATA: {
|
||||||
DEBUGF(fprintf(stderr, "DATA\n"));
|
DEBUGF(fprintf(stderr, "DATA\n"));
|
||||||
|
@ -3841,6 +3877,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||||
if(iframe->payloadleft) {
|
if(iframe->payloadleft) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* Clear PAD_HIGH and PAD_LOW, because we rely on those flags
|
||||||
|
in the next CONTINUATION frame. */
|
||||||
|
iframe->frame.hd.flags &=
|
||||||
|
~(NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW);
|
||||||
if((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) {
|
if((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) {
|
||||||
inbound_frame_reset_left(iframe, NGHTTP2_FRAME_HEAD_LENGTH);
|
inbound_frame_reset_left(iframe, NGHTTP2_FRAME_HEAD_LENGTH);
|
||||||
iframe->padlen = 0;
|
iframe->padlen = 0;
|
||||||
|
@ -3950,6 +3990,9 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->buf);
|
nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->buf);
|
||||||
iframe->payloadleft = cont_hd.length;
|
iframe->payloadleft = cont_hd.length;
|
||||||
|
|
||||||
|
DEBUGF(fprintf(stderr, "payloadlen=%zu\n", iframe->payloadleft));
|
||||||
|
|
||||||
if(cont_hd.type != NGHTTP2_CONTINUATION ||
|
if(cont_hd.type != NGHTTP2_CONTINUATION ||
|
||||||
cont_hd.stream_id != iframe->frame.hd.stream_id) {
|
cont_hd.stream_id != iframe->frame.hd.stream_id) {
|
||||||
DEBUGF(fprintf(stderr, "expected stream_id=%d, type=%d, but "
|
DEBUGF(fprintf(stderr, "expected stream_id=%d, type=%d, but "
|
||||||
|
|
|
@ -199,6 +199,8 @@ int main(int argc, char* argv[])
|
||||||
test_nghttp2_session_data_backoff_by_high_pri_frame) ||
|
test_nghttp2_session_data_backoff_by_high_pri_frame) ||
|
||||||
!CU_add_test(pSuite, "session_pack_data_with_padding",
|
!CU_add_test(pSuite, "session_pack_data_with_padding",
|
||||||
test_nghttp2_session_pack_data_with_padding) ||
|
test_nghttp2_session_pack_data_with_padding) ||
|
||||||
|
!CU_add_test(pSuite, "session_pack_headers_with_padding",
|
||||||
|
test_nghttp2_session_pack_headers_with_padding) ||
|
||||||
!CU_add_test(pSuite, "pack_settings_payload",
|
!CU_add_test(pSuite, "pack_settings_payload",
|
||||||
test_nghttp2_pack_settings_payload) ||
|
test_nghttp2_pack_settings_payload) ||
|
||||||
!CU_add_test(pSuite, "frame_pack_headers",
|
!CU_add_test(pSuite, "frame_pack_headers",
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
#define OB_DATA(ITEM) nghttp2_outbound_item_get_data_frame(ITEM)
|
#define OB_DATA(ITEM) nghttp2_outbound_item_get_data_frame(ITEM)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t buf[4096];
|
uint8_t buf[65535];
|
||||||
size_t length;
|
size_t length;
|
||||||
} accumulator;
|
} accumulator;
|
||||||
|
|
||||||
|
@ -4039,6 +4039,54 @@ void test_nghttp2_session_pack_data_with_padding(void)
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_session_pack_headers_with_padding(void)
|
||||||
|
{
|
||||||
|
nghttp2_session *session, *sv_session;
|
||||||
|
accumulator acc;
|
||||||
|
my_user_data ud;
|
||||||
|
nghttp2_session_callbacks callbacks, sv_callbacks;
|
||||||
|
nghttp2_nv nva[8190];
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for(i = 0; i < ARRLEN(nva); ++i) {
|
||||||
|
nva[i].name = (uint8_t*)":path";
|
||||||
|
nva[i].namelen = 5;
|
||||||
|
nva[i].value = (uint8_t*)"/";
|
||||||
|
nva[i].valuelen = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
|
callbacks.send_callback = accumulator_send_callback;
|
||||||
|
callbacks.on_frame_send_callback = on_frame_send_callback;
|
||||||
|
callbacks.select_padding_callback = select_padding_callback;
|
||||||
|
|
||||||
|
acc.length = 0;
|
||||||
|
ud.acc = &acc;
|
||||||
|
|
||||||
|
memset(&sv_callbacks, 0, sizeof(sv_callbacks));
|
||||||
|
sv_callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||||
|
|
||||||
|
nghttp2_session_client_new(&session, &callbacks, &ud);
|
||||||
|
nghttp2_session_server_new(&sv_session, &sv_callbacks, &ud);
|
||||||
|
|
||||||
|
ud.padding_boundary = 16385;
|
||||||
|
|
||||||
|
CU_ASSERT(0 ==
|
||||||
|
nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT,
|
||||||
|
nva, ARRLEN(nva), NULL, NULL));
|
||||||
|
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||||
|
|
||||||
|
CU_ASSERT(acc.length > NGHTTP2_MAX_FRAME_LENGTH);
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
CU_ASSERT((ssize_t)acc.length ==
|
||||||
|
nghttp2_session_mem_recv(sv_session, acc.buf, acc.length));
|
||||||
|
CU_ASSERT(1 == ud.frame_recv_cb_called);
|
||||||
|
CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(sv_session));
|
||||||
|
|
||||||
|
nghttp2_session_del(sv_session);
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
}
|
||||||
|
|
||||||
void test_nghttp2_pack_settings_payload(void)
|
void test_nghttp2_pack_settings_payload(void)
|
||||||
{
|
{
|
||||||
nghttp2_settings_entry iv[2];
|
nghttp2_settings_entry iv[2];
|
||||||
|
|
|
@ -90,6 +90,7 @@ void test_nghttp2_session_get_effective_local_window_size(void);
|
||||||
void test_nghttp2_session_set_option(void);
|
void test_nghttp2_session_set_option(void);
|
||||||
void test_nghttp2_session_data_backoff_by_high_pri_frame(void);
|
void test_nghttp2_session_data_backoff_by_high_pri_frame(void);
|
||||||
void test_nghttp2_session_pack_data_with_padding(void);
|
void test_nghttp2_session_pack_data_with_padding(void);
|
||||||
|
void test_nghttp2_session_pack_headers_with_padding(void);
|
||||||
void test_nghttp2_pack_settings_payload(void);
|
void test_nghttp2_pack_settings_payload(void);
|
||||||
|
|
||||||
#endif /* NGHTTP2_SESSION_TEST_H */
|
#endif /* NGHTTP2_SESSION_TEST_H */
|
||||||
|
|
Loading…
Reference in New Issue