Skip to content
Closed
120 changes: 77 additions & 43 deletions src/ngx_http_modsecurity_body_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,36 @@ ngx_http_modsecurity_body_filter_init(void)

return NGX_OK;
}

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;
ngx_chain_t *chain = in;
ngx_int_t ret;
ngx_pool_t *old_pool;
ngx_int_t is_request_processed = 0;
#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
ngx_http_modsecurity_conf_t *loc_cf = NULL;
ngx_list_part_t *part = &r->headers_out.headers.part;
ngx_table_elt_t *data = part->elts;
ngx_uint_t i = 0;
#endif

if (in == NULL) {
if (in == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "MDS input chain is null");

return ngx_http_next_body_filter(r, in);
}
}

/* get context for request */
ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);

dd("body filter, recovering ctx: %p", ctx);

if (ctx == NULL) {
if (ctx == NULL || r->filter_finalize || ctx->response_body_filtered) {
return ngx_http_next_body_filter(r, in);
}


#if defined(MODSECURITY_SANITY_CHECKS) && (MODSECURITY_SANITY_CHECKS)
loc_cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity_module);
if (loc_cf != NULL && loc_cf->sanity_checks_enabled != NGX_CONF_UNSET)
Expand Down Expand Up @@ -130,56 +134,86 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
dd("%d header(s) were not inspected by ModSecurity, so exiting", worth_to_fail);
return ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module, NGX_HTTP_INTERNAL_SERVER_ERROR);
&ngx_http_modsecurity_module, NGX_HTTP_INTERNAL_SERVER_ERROR);
}
}
#endif

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;
for (chain = in; chain != NULL; chain = chain->next) {

ngx_buf_t *copy_buf;
ngx_chain_t* copy_chain;
is_request_processed = chain->buf->last_buf;
ngx_int_t data_size = chain->buf->last - chain->buf->pos;
u_char *data = chain->buf->pos;
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;

for (chain = in; chain != NULL; chain = chain->next)
{
u_char *data = chain->buf->start;
copy_chain = ngx_alloc_chain_link(r->pool);
if (copy_chain == NULL) {
return NGX_ERROR;
}

msc_append_response_body(ctx->modsec_transaction, data, chain->buf->end - 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);
copy_buf = ngx_calloc_buf(r->pool);
if (copy_buf == NULL) {
return NGX_ERROR;
}
copy_buf->start = ngx_pcalloc(r->pool, data_size);
if (copy_buf->start == NULL) {
return NGX_ERROR;
}
ngx_memcpy(copy_buf->start, chain->buf->pos, data_size);
copy_buf->pos = copy_buf->start ;
copy_buf->end = copy_buf->pos + data_size ;
copy_buf->last = copy_buf->pos + ngx_buf_size(chain->buf);
copy_buf->temporary = (chain->buf->temporary == 1) ? 1 : 0;
copy_buf->memory = (chain->buf->memory == 1) ? 1 : 0;
copy_chain->buf = copy_buf;
copy_chain->buf->last_buf = 1;
copy_chain->next = NULL;
chain->buf->pos = chain->buf->last;
if (ctx->temp_chain == NULL) {
ctx->temp_chain = copy_chain;
} else {
if (ctx->current_chain == NULL) {
ctx->temp_chain->next = copy_chain;
ctx->temp_chain->buf->last_buf = 0;
} else {
ctx->current_chain->next = copy_chain;
ctx->current_chain->buf->last_buf = 0;
}
ctx->current_chain = copy_chain;
}
}

if (is_request_processed) {
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);

/* 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) {
if (ret < NGX_HTTP_BAD_REQUEST && ctx->header_pt != NULL){
ctx->header_pt(r);
}
else {
ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module
, ret);
}
} else if (ret < 0) {
return ngx_http_filter_finalize_request(r,
&ngx_http_modsecurity_module, NGX_HTTP_INTERNAL_SERVER_ERROR);
&ngx_http_modsecurity_module, NGX_HTTP_INTERNAL_SERVER_ERROR);
}
ctx->response_body_filtered = 1;
if (ctx->header_pt != NULL)
ctx->header_pt(r);
return ngx_http_next_body_filter(r, ctx->temp_chain);
} else {
return NGX_AGAIN;
}
else
{
dd("buffer was not fully loaded! ctx: %p", ctx);
}

/* XXX: xflt_filter() -- return NGX_OK here */
return ngx_http_next_body_filter(r, in);
}
5 changes: 4 additions & 1 deletion src/ngx_http_modsecurity_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ typedef struct {
ngx_str_t value;
} ngx_http_modsecurity_header_t;


typedef struct {
ngx_http_request_t *r;
Transaction *modsec_transaction;
Expand All @@ -52,6 +51,10 @@ typedef struct {
unsigned waiting_more_body:1;
unsigned body_requested:1;
unsigned processed:1;
ngx_http_output_header_filter_pt header_pt;
ngx_chain_t* temp_chain;
ngx_chain_t* current_chain;
unsigned response_body_filtered:1;
} ngx_http_modsecurity_ctx_t;


Expand Down
19 changes: 13 additions & 6 deletions src/ngx_http_modsecurity_header_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -542,10 +542,17 @@ ngx_http_modsecurity_header_filter(ngx_http_request_t *r)
*
*/

/*
* The line below is commented to make the spdy test to work
*/
//r->headers_out.content_length_n = -1;

return ngx_http_next_header_filter(r);
/*
* The line below is commented to make the spdy test to work
*/
//r->headers_out.content_length_n = -1;

if (ctx->response_body_filtered){
return ngx_http_next_header_filter(r);
}
else {
ctx->header_pt = ngx_http_next_header_filter;
r->headers_out.content_length_n = -1;
return NGX_AGAIN;
}
}
3 changes: 2 additions & 1 deletion src/ngx_http_modsecurity_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ ngx_http_modsecurity_create_ctx(ngx_http_request_t *r)
ctx->modsec_transaction = msc_new_transaction(cf->modsec, loc_cf->rules_set, r->connection->log);

dd("transaction created");


ctx->response_body_filtered = 0;
ngx_http_set_ctx(r, ctx, ngx_http_modsecurity_module);

cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_modsecurity_ctx_t));
Expand Down
4 changes: 2 additions & 2 deletions src/ngx_http_modsecurity_pre_access.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ ngx_http_modsecurity_pre_access_handler(ngx_http_request_t *r)

while (chain && !already_inspected)
{
u_char *data = chain->buf->start;
u_char *data = chain->buf->pos;

msc_append_request_body(ctx->modsec_transaction, data,
chain->buf->last - chain->buf->pos);
chain->buf->last - data);

if (chain->buf->last_buf) {
break;
Expand Down
8 changes: 4 additions & 4 deletions tests/modsecurity-proxy.t
Original file line number Diff line number Diff line change
Expand Up @@ -116,25 +116,25 @@ unlike(http_head('/'), qr/SEE-THIS/, 'proxy head request');
like(http_get('/phase1?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 1');
like(http_get('/phase2?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 2');
like(http_get('/phase3?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 3');
is(http_get('/phase4?what=redirect302'), '', 'redirect 302 - phase 4');
like(http_get('/phase4?what=redirect302'), qr/404/, 'redirect 302 - phase 4');

# Redirect (301)
like(http_get('/phase1?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 1');
like(http_get('/phase2?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 2');
like(http_get('/phase3?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 3');
is(http_get('/phase4?what=redirect301'), '', 'redirect 301 - phase 4');
like(http_get('/phase4?what=redirect301'), qr/404/, 'redirect 301 - phase 4');

# Block (401)
like(http_get('/phase1?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 1');
like(http_get('/phase2?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 2');
like(http_get('/phase3?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 3');
is(http_get('/phase4?what=block401'), '', 'block 401 - phase 4');
like(http_get('/phase4?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 4');

# Block (403)
like(http_get('/phase1?what=block403'), qr/403 Forbidden/, 'block 403 - phase 1');
like(http_get('/phase2?what=block403'), qr/403 Forbidden/, 'block 403 - phase 2');
like(http_get('/phase3?what=block403'), qr/403 Forbidden/, 'block 403 - phase 3');
is(http_get('/phase4?what=block403'), '', 'block 403 - phase 4');
like(http_get('/phase4?what=block403'), qr/403 Forbidden/, 'block 403 - phase 4');

# Nothing to detect
like(http_get('/phase1?what=nothing'), qr/phase1\?what=nothing\' not found/, 'nothing phase 1');
Expand Down
8 changes: 4 additions & 4 deletions tests/modsecurity.t
Original file line number Diff line number Diff line change
Expand Up @@ -123,25 +123,25 @@ $t->plan(20);
like(http_get('/phase1?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 1');
like(http_get('/phase2?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 2');
like(http_get('/phase3?what=redirect302'), qr/302 Moved Temporarily/, 'redirect 302 - phase 3');
is(http_get('/phase4?what=redirect302'), '', 'redirect 302 - phase 4');
like(http_get('/phase4?what=redirect302'), qr/200/, 'redirect 302 - phase 4');

# Redirect (301)
like(http_get('/phase1?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 1');
like(http_get('/phase2?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 2');
like(http_get('/phase3?what=redirect301'), qr/301 Moved Permanently/, 'redirect 301 - phase 3');
is(http_get('/phase4?what=redirect301'), '', 'redirect 301 - phase 4');
like(http_get('/phase4?what=redirect301'), qr/200/, 'redirect 301 - phase 4');

# Block (401)
like(http_get('/phase1?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 1');
like(http_get('/phase2?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 2');
like(http_get('/phase3?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 3');
is(http_get('/phase4?what=block401'), '', 'block 401 - phase 4');
like(http_get('/phase4?what=block401'), qr/401 Unauthorized/, 'block 401 - phase 4');

# Block (403)
like(http_get('/phase1?what=block403'), qr/403 Forbidden/, 'block 403 - phase 1');
like(http_get('/phase2?what=block403'), qr/403 Forbidden/, 'block 403 - phase 2');
like(http_get('/phase3?what=block403'), qr/403 Forbidden/, 'block 403 - phase 3');
is(http_get('/phase4?what=block403'), '', 'block 403 - phase 4');
like(http_get('/phase4?what=block403'), qr/403 Forbidden/, 'block 403 - phase 4');

# Nothing to detect
like(http_get('/phase1?what=nothing'), qr/should be moved\/blocked before this./, 'nothing phase 1');
Expand Down