Skip to content

Commit

Permalink
Fixed processing of response body chunks in ngx_http_modsecurity_body…
Browse files Browse the repository at this point in the history
…_filter.

A body filter function (ngx_http_modsecurity_body_filter in our case)
can be called by Nginx several times during request processing. And
each time with it own unique set of chained buf pointers.

For example, suppose a complete response consists of this chain of data:
    A->B->C->D->E
Ngix may (and actually does, as verified by me in gdb) call body filter two
times like this:
    handler(r, in = A->B->C)
    handler(r, in = D->E), E has last_buf set

Current implementation delays feeding chain->buf to msc_append_response_body
until it comes upon a chain with buf->last_buf set. So we loose chain containing
A->B->C sequence. We must process body bufs as soon as we see them in body
handler otherwise we will not see them again.

N.B. You have PR owasp-modsecurity#84 pending. It goes further and fixes the problem when
a blocking decision is made after headers were sent. I intentionally retained
current (buggy) behavior to make my patch less intrusive and easier to review.
Besides owasp-modsecurity#84 impose an excessive memory usage due to a complete copy of all
bufs passed through body filter (we have sometimes 500K and more replies in our
applications) - I will elaborate on this in code review for owasp-modsecurity#84.
  • Loading branch information
turchanov authored and Felipe Zimmerle committed May 15, 2018
1 parent 3d82ee2 commit 146054f
Showing 1 changed file with 25 additions and 30 deletions.
55 changes: 25 additions & 30 deletions src/ngx_http_modsecurity_body_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ ngx_http_modsecurity_body_filter_init(void)
ngx_int_t
ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
int buffer_fully_loadead = 0;
ngx_chain_t *chain = in;
ngx_http_modsecurity_ctx_t *ctx = NULL;
#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
Expand Down Expand Up @@ -135,47 +134,43 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
}
#endif

int is_request_processed = 0;
for (; chain != NULL; chain = chain->next)
{
/* XXX: chain->buf->last_buf || chain->buf->last_in_chain */
if (chain->buf->last_buf) {
buffer_fully_loadead = 1;
u_char *data = chain->buf->pos;
int ret;

msc_append_response_body(ctx->modsec_transaction, data, chain->buf->last - data);
ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
if (ret > 0) {
return ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module, ret);
}
}

if (buffer_fully_loadead == 1)
{
int ret;
ngx_pool_t *old_pool;
/* XXX: chain->buf->last_buf || chain->buf->last_in_chain */
is_request_processed = chain->buf->last_buf;

for (chain = in; chain != NULL; chain = chain->next)
{
u_char *data = chain->buf->pos;
if (is_request_processed) {
ngx_pool_t *old_pool;

old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
msc_process_response_body(ctx->modsec_transaction);
ngx_http_modsecurity_pcre_malloc_done(old_pool);

msc_append_response_body(ctx->modsec_transaction, data, chain->buf->last - data);
/* XXX: I don't get how body from modsec being transferred to nginx's buffer. If so - after adjusting of nginx's
XXX: body we can proceed to adjust body size (content-length). see xslt_body_filter() for example */
ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
if (ret > 0) {
return ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module, ret);
return ret;
}
}

old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
msc_process_response_body(ctx->modsec_transaction);
ngx_http_modsecurity_pcre_malloc_done(old_pool);
else if (ret < 0) {
return ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module, NGX_HTTP_INTERNAL_SERVER_ERROR);

/* XXX: I don't get how body from modsec being transferred to nginx's buffer. If so - after adjusting of nginx's
XXX: body we can proceed to adjust body size (content-length). see xslt_body_filter() for example */
ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
if (ret > 0) {
return ret;
}
else if (ret < 0) {
return ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module, NGX_HTTP_INTERNAL_SERVER_ERROR);
}
}
}
else
if (!is_request_processed)
{
dd("buffer was not fully loaded! ctx: %p", ctx);
}
Expand Down

0 comments on commit 146054f

Please sign in to comment.