diff --git a/nginx/modsecurity/apr_bucket_nginx.c b/nginx/modsecurity/apr_bucket_nginx.c index 62eb59a7..8dc98d50 100644 --- a/nginx/modsecurity/apr_bucket_nginx.c +++ b/nginx/modsecurity/apr_bucket_nginx.c @@ -154,9 +154,11 @@ ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool) { } ngx_int_t -move_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *pool, ngx_int_t last_buf) { +move_chain_to_brigade(ngx_chain_t *chain_orig, apr_bucket_brigade *bb, ngx_pool_t *pool, ngx_int_t last_buf) { apr_bucket *e; - ngx_chain_t *cl; + ngx_chain_t *cl; + + ngx_chain_t *chain = chain_orig; while (chain) { e = ngx_buf_to_apr_bucket(chain->buf, bb->p, bb->bucket_alloc); @@ -168,7 +170,6 @@ move_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *po if (chain->buf->last_buf) { e = apr_bucket_eos_create(bb->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); - chain->buf->last_buf = 0; return NGX_OK; } cl = chain; @@ -181,7 +182,6 @@ move_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *po APR_BRIGADE_INSERT_TAIL(bb, e); return NGX_OK; } - return NGX_AGAIN; } @@ -215,8 +215,10 @@ move_brigade_to_chain(apr_bucket_brigade *bb, ngx_chain_t **ll, ngx_pool_t *pool } cl->buf->last_buf = 1; + cl->next = NULL; *ll = cl; } else { + cl->next = NULL; cl->buf->last_buf = 1; } apr_brigade_cleanup(bb); @@ -243,6 +245,7 @@ move_brigade_to_chain(apr_bucket_brigade *bb, ngx_chain_t **ll, ngx_pool_t *pool ll = &cl->next; } + apr_brigade_cleanup(bb); /* no eos or error */ return NGX_ERROR; diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 7c139531..fc8555f5 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -18,6 +18,10 @@ #include + +/* Those are defined twice, lets keep it defined just once by `undef` + * the first one. + */ #undef CR #undef LF #undef CRLF @@ -25,22 +29,42 @@ #include "api.h" #define NOTE_NGINX_REQUEST_CTX "nginx-ctx" +#define TXID_SIZE 25 + +#define tuxb + +/* + * If NGX_HTTP_MODSECURITY_PREACCESS_HANDLER_ONLY is defined, this module will + * not process the body and header filter, instead it will only process the + * preaccess request handler. This is useful to debug the module and check if + * reference counters (r->main->count) and connections are being handled as + * expected. However, do not expect to have ModSecurity fully workable with + * this variable set. Use only for debugging. + * + */ +/* +#define NGX_HTTP_MODSECURITY_PREACCESS_HANDLER_ONLY + */ typedef struct { - ngx_flag_t enable; - directory_config *config; + ngx_flag_t enable; + directory_config *config; - ngx_str_t *file; - ngx_uint_t line; + ngx_str_t *file; + ngx_uint_t line; } ngx_http_modsecurity_loc_conf_t; typedef struct { - ngx_http_request_t *r; - conn_rec *connection; - request_rec *req; + ngx_http_request_t *r; + conn_rec *connection; + request_rec *req; - apr_bucket_brigade *brigade; - unsigned complete; + apr_bucket_brigade *brigade; + + unsigned complete:1; + unsigned request_processed:1; + unsigned waiting_more_body:1; + unsigned body_requested:1; } ngx_http_modsecurity_ctx_t; @@ -48,14 +72,19 @@ typedef struct { ** Module's registred function/handlers. */ static ngx_int_t ngx_http_modsecurity_handler(ngx_http_request_t *r); -static void ngx_http_modsecurity_body_handler(ngx_http_request_t *r); +static void ngx_http_modsecurity_request_read(ngx_http_request_t *r); + +#ifndef NGX_HTTP_MODSECURITY_PREACCESS_HANDLER_ONLY static ngx_int_t ngx_http_modsecurity_header_filter(ngx_http_request_t *r); static ngx_int_t ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in); +#endif static ngx_int_t ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf); static ngx_int_t ngx_http_modsecurity_init(ngx_conf_t *cf); static ngx_int_t ngx_http_modsecurity_init_process(ngx_cycle_t *cycle); + static void *ngx_http_modsecurity_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_modsecurity_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); + static char *ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_modsecurity_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -67,9 +96,25 @@ static void ngx_http_modsecurity_cleanup(void *data); static int ngx_http_modsecurity_save_headers_in_visitor(void *data, const char *key, const char *value); static int ngx_http_modsecurity_save_headers_out_visitor(void *data, const char *key, const char *value); +/* +** This is a temporary hack to make PCRE work with ModSecurity +** nginx hijacks pcre_malloc and pcre_free, so we have to re-hijack them +*/ +extern apr_pool_t *pool; +static void * +modsec_pcre_malloc(size_t size) +{ + return apr_palloc(pool, size); +} -/* command handled by the module */ -static ngx_command_t ngx_http_modsecurity_commands[] = { +static void +modsec_pcre_free(void *ptr) +{ +} + +static server_rec *modsec_server = NULL; + +static ngx_command_t ngx_http_modsecurity_commands[] = { { ngx_string("ModSecurityConfig"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_modsecurity_config, @@ -86,10 +131,6 @@ static ngx_command_t ngx_http_modsecurity_commands[] = { ngx_null_command }; -/* -** handlers for configuration phases of the module -*/ - static ngx_http_module_t ngx_http_modsecurity_ctx = { ngx_http_modsecurity_preconfiguration, /* preconfiguration */ ngx_http_modsecurity_init, /* postconfiguration */ @@ -104,7 +145,6 @@ static ngx_http_module_t ngx_http_modsecurity_ctx = { ngx_http_modsecurity_merge_loc_conf /* merge location configuration */ }; - ngx_module_t ngx_http_modsecurity = { NGX_MODULE_V1, &ngx_http_modsecurity_ctx, /* module context */ @@ -120,9 +160,11 @@ ngx_module_t ngx_http_modsecurity = { NGX_MODULE_V1_PADDING }; -static ngx_http_output_header_filter_pt ngx_http_next_header_filter; -static ngx_http_output_body_filter_pt ngx_http_next_body_filter; +#ifndef NGX_HTTP_MODSECURITY_PREACCESS_HANDLER_ONLY +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; +#endif static ngx_http_upstream_t ngx_http_modsecurity_upstream; static struct { @@ -140,7 +182,6 @@ static struct { {NULL, ngx_null_string} }; - static inline u_char * ngx_pstrdup0(ngx_pool_t *pool, ngx_str_t *src) { @@ -148,7 +189,7 @@ ngx_pstrdup0(ngx_pool_t *pool, ngx_str_t *src) dst = ngx_pnalloc(pool, src->len + 1); if (dst == NULL) { - return NULL; + NGX_HTTP_MODSECURITY_ return NULL; } ngx_memcpy(dst, src->data, src->len); @@ -157,7 +198,6 @@ ngx_pstrdup0(ngx_pool_t *pool, ngx_str_t *src) return dst; } - static inline int ngx_http_modsecurity_method_number(unsigned int nginx) { @@ -329,8 +369,6 @@ ngx_http_modsecurity_load_headers_in(ngx_http_request_t *r) req->user = (char *)ngx_pstrdup0(r->pool, &r->headers_in.user); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: load headers in done"); @@ -346,7 +384,6 @@ ngx_http_modsecurity_save_headers_in(ngx_http_request_t *r) /* clean up headers_in */ ngx_memzero(&r->headers_in, sizeof(ngx_http_headers_in_t)); - if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) @@ -457,16 +494,25 @@ ngx_http_modsecurity_load_request_body(ngx_http_request_t *r) { ngx_http_modsecurity_ctx_t *ctx; + + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: loading request body."); + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); modsecSetBodyBrigade(ctx->req, ctx->brigade); if (r->request_body == NULL || r->request_body->bufs == NULL) { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: request was null."); return move_chain_to_brigade(NULL, ctx->brigade, r->pool, 1); } if (move_chain_to_brigade(r->request_body->bufs, ctx->brigade, r->pool, 1) != NGX_OK) { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: failed to move chain to brigade."); + return NGX_ERROR; } @@ -475,106 +521,25 @@ ngx_http_modsecurity_load_request_body(ngx_http_request_t *r) return NGX_OK; } - static ngx_inline ngx_int_t ngx_http_modsecurity_save_request_body(ngx_http_request_t *r) { ngx_http_modsecurity_ctx_t *ctx; apr_off_t content_length; ngx_buf_t *buf; - ngx_http_core_srv_conf_t *cscf; - size_t size; - ngx_http_connection_t *hc; - ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); apr_brigade_length(ctx->brigade, 0, &content_length); - if (r->header_in->end - r->header_in->last >= content_length) { - /* use r->header_in */ - - if (ngx_buf_size(r->header_in)) { - /* move to the end */ - ngx_memmove(r->header_in->pos + content_length, - r->header_in->pos, - ngx_buf_size(r->header_in)); - } - - if (apr_brigade_flatten(ctx->brigade, - (char *)r->header_in->pos, - (apr_size_t *)&content_length) != APR_SUCCESS) { - return NGX_ERROR; - } - - apr_brigade_cleanup(ctx->brigade); - - r->header_in->last += content_length; - - return NGX_OK; - } - - if (ngx_buf_size(r->header_in)) { - - /* - * ngx_http_set_keepalive will reuse r->header_in if - * (r->header_in != c->buffer && r->header_in.last != r->header_in.end), - * so we need this code block. - * see ngx_http_set_keepalive, ngx_http_alloc_large_header_buffer - */ - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - - size = ngx_max(cscf->large_client_header_buffers.size, - (size_t)content_length + ngx_buf_size(r->header_in)); - - hc = r->http_connection; - - if (hc->nfree && size == cscf->large_client_header_buffers.size) { - - buf = hc->free[--hc->nfree]; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "ModSecurity: use http free large header buffer: %p %uz", - buf->pos, buf->end - buf->last); - - } else if (hc->nbusy < cscf->large_client_header_buffers.num) { - - if (hc->busy == NULL) { - hc->busy = ngx_palloc(r->connection->pool, - cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *)); - } - - if (hc->busy == NULL) { - return NGX_ERROR; - } else { - buf = ngx_create_temp_buf(r->connection->pool, size); - } - } else { - /* TODO: how to deal this case ? */ - return NGX_ERROR; - } - - } else { - - buf = ngx_create_temp_buf(r->pool, (size_t) content_length); - } - - if (buf == NULL) { - return NGX_ERROR; - } - - if (apr_brigade_flatten(ctx->brigade, (char *)buf->pos, - (apr_size_t *)&content_length) != APR_SUCCESS) { - return NGX_ERROR; - } - + buf = ngx_create_temp_buf(ctx->r->pool, (size_t) content_length); + apr_brigade_flatten(ctx->brigade, (char *)buf->pos, (apr_size_t *)&content_length); apr_brigade_cleanup(ctx->brigade); buf->last += content_length; - - ngx_memcpy(buf->last, r->header_in->pos, ngx_buf_size(r->header_in)); - buf->last += ngx_buf_size(r->header_in); - r->header_in = buf; + r->headers_in.content_length_n = content_length; + +ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSec: Content length: %O, Content length n: %O", content_length, r->headers_in.content_length_n); return NGX_OK; } @@ -797,6 +762,114 @@ ngx_http_modsecurity_status(ngx_http_request_t *r, int status) } +/* + * @brief Process request whenever `request header` and `body` are ready for. + * + * This function provides calls ModSecurity standalone library in the necessary + * order to analisys: `request headers` and `request body`. + * + * @note ModSecurity state can be changed by ctl:ruleEngine=off + * + * @param r pointer to ngx_http_request_t. + * + */ +static void +ngx_http_modsecurity_process_request(ngx_http_request_t *r) +{ + ngx_int_t rc = 0; + ngx_http_modsecurity_ctx_t *ctx; + int load_request_body = 0; + + ctx = ngx_http_get_module_pool_ctx(r, ngx_http_modsecurity); + + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: starting to process the request..."); + + if (ngx_http_modsecurity_load_request(r) != NGX_OK) { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: failed while loading the request."); + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + goto terminate; + } + + if (ngx_http_modsecurity_load_headers_in(r) != NGX_OK) { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: failed while loading the headers."); + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + goto terminate; + } + + rc = modsecProcessRequestHeaders(ctx->req); + rc = ngx_http_modsecurity_status(r, rc); + if (rc != NGX_DECLINED) { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: failed while processing the request headers"); + ngx_http_finalize_request(r, rc); + } + + /* Here we check if ModSecurity is enabled or disabled again. + * This is a kind of hack. ModSecurity can be disable by: + * ctl:ruleEngine=off, which shouldn't be a problem. However, due to + * the way that standalone is handling this situation this lead us to + * a problem that may end up by freezing the connection. As we don't + * want that we verify if it was disabled prior to load the request body. + * + * Changes on standalone should be coordenated with others like IIS, so + * leaving this dirty hack here. + */ + if (r->method == NGX_HTTP_POST && + modsecIsRequestBodyAccessEnabled(ctx->req) && + modsecContextState(ctx->req) != MODSEC_DISABLED) { + + load_request_body = 1; + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: request body will be processed..."); + } + + if (load_request_body == 1) { + rc = ngx_http_modsecurity_load_request_body(r); + if (rc != NGX_OK) + { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: failed while loading the request body."); + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + + } + + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: processing request body..."); + rc = modsecProcessRequestBody(ctx->req); + rc = ngx_http_modsecurity_status(r, rc); + if (rc != NGX_DECLINED) + { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: finalizing the connection after process the " \ + "request body."); + + ngx_http_finalize_request(r, rc); + } + if (load_request_body == 1) { + if (ngx_http_modsecurity_save_request_body(r) != NGX_OK) + { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: failed while saving the request body"); + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK) + { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: failed while saving the headers in"); + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + } +terminate: + return; +} + + + /* create loc conf struct */ static void * ngx_http_modsecurity_create_loc_conf(ngx_conf_t *cf) @@ -849,24 +922,6 @@ modsecLog(void *obj, int level, char *str) } } -/* -** This is a temporary hack to make PCRE work with ModSecurity -** nginx hijacks pcre_malloc and pcre_free, so we have to re-hijack them -*/ -extern apr_pool_t *pool; - -static void * -modsec_pcre_malloc(size_t size) -{ - return apr_palloc(pool, size); -} - -static void -modsec_pcre_free(void *ptr) -{ -} - -static server_rec *modsec_server = NULL; static ngx_int_t ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) @@ -906,7 +961,6 @@ ngx_http_modsecurity_terminate(ngx_cycle_t *cycle) } } - static ngx_int_t ngx_http_modsecurity_init(ngx_conf_t *cf) { @@ -917,24 +971,29 @@ ngx_http_modsecurity_init(ngx_conf_t *cf) cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } + *h = ngx_http_modsecurity_handler; +#ifndef NGX_HTTP_MODSECURITY_PREACCESS_HANDLER_ONLY ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_modsecurity_header_filter; ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_modsecurity_body_filter; +#endif ngx_memzero(&ngx_http_modsecurity_upstream, sizeof(ngx_http_upstream_t)); +#ifdef MODSEC_UPSTREAM_CACHABLE ngx_http_modsecurity_upstream.cacheable = 1; +#endif return NGX_OK; } - static ngx_int_t ngx_http_modsecurity_init_process(ngx_cycle_t *cycle) { @@ -944,119 +1003,122 @@ ngx_http_modsecurity_init_process(ngx_cycle_t *cycle) return NGX_OK; } - -/* -** [ENTRY POINT] does : this function called by nginx from the request handler -*/ static ngx_int_t -ngx_http_modsecurity_handler(ngx_http_request_t *r) -{ - ngx_http_modsecurity_loc_conf_t *cf; - ngx_http_modsecurity_ctx_t *ctx; - ngx_int_t rc; +ngx_http_modsecurity_handler(ngx_http_request_t *r) { + ngx_int_t rc = NULL; + ngx_http_modsecurity_ctx_t *ctx = NULL; + ngx_http_modsecurity_loc_conf_t *cf = NULL; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: Catching a new access phase handler. Count: %d", r->main->count); cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); /* Process only main request */ if (r != r->main || !cf->enable) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: ModSecurity is not enabled or not a main request."); + return NGX_DECLINED; } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: handler"); - - /* create / retrive request ctx */ - if (r->internal) { - - ctx = ngx_http_get_module_pool_ctx(r, ngx_http_modsecurity); - - if (ctx) { - /* we have already processed the request headers */ - ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); - return NGX_DECLINED; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: request pool ctx empty"); - } - - ctx = ngx_http_modsecurity_create_ctx(r); - if (ctx == NULL) { - - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); - - if (ngx_http_set_pool_ctx(r, ctx, ngx_http_modsecurity) != NGX_OK) { - return NGX_ERROR; - } - - /* load request to request rec */ - if (ngx_http_modsecurity_load_request(r) != NGX_OK - || ngx_http_modsecurity_load_headers_in(r) != NGX_OK) { - - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - /* processing request headers */ - rc = ngx_http_modsecurity_status(r, modsecProcessRequestHeaders(ctx->req)); - - if (rc != NGX_DECLINED) { - return rc; - } - - if (modsecContextState(ctx->req) == MODSEC_DISABLED) { - return NGX_DECLINED; - } - - if (r->method == NGX_HTTP_POST - && modsecIsRequestBodyAccessEnabled(ctx->req) ) { - - /* read POST request body, should we process PUT? */ - rc = ngx_http_read_client_request_body(r, ngx_http_modsecurity_body_handler); - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - - return NGX_DONE; - } - - /* other method */ - return ngx_http_modsecurity_status(r, modsecProcessRequestBody(ctx->req)); -} - - -static void -ngx_http_modsecurity_body_handler(ngx_http_request_t *r) -{ - ngx_http_modsecurity_ctx_t *ctx; - ngx_int_t rc; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: body handler"); - ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); - if (ngx_http_modsecurity_load_request_body(r) != NGX_OK) { - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: Recovering ctx: %p", ctx); + + if (ctx == NULL) { + ctx = ngx_http_modsecurity_create_ctx(r); + + ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); + + if (ngx_http_set_pool_ctx(r, ctx, ngx_http_modsecurity) != NGX_OK) { + return NGX_ERROR; + } + + } - rc = ngx_http_modsecurity_status(r, modsecProcessRequestBody(ctx->req)); + if (ctx == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: ctx is null, nothing we can do, returning."); - if (rc != NGX_DECLINED) { - return ngx_http_finalize_request(r, rc); + return NGX_HTTP_INTERNAL_SERVER_ERROR; } - if (ngx_http_modsecurity_save_request_body(r) != NGX_OK - || ngx_http_modsecurity_save_headers_in(r) != NGX_OK ) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: ctx is now: %p / count: %d", ctx, r->main->count); - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + if (modsecContextState(ctx->req) == MODSEC_DISABLED) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: ModSecurity was disabled, returning....", ctx); + + return NGX_DECLINED; } - r->phase_handler++; - ngx_http_core_run_phases(r); - ngx_http_finalize_request(r, NGX_DONE); + if (ctx->waiting_more_body == 1) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: waiting for more data before proceed. / count: %d", r->main->count); + + return NGX_DONE; + } + + if (ctx->body_requested == 0) { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: asking for the request body, if any."); + + ctx->body_requested = 1; + rc = ngx_http_read_client_request_body(r, + ngx_http_modsecurity_request_read); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + if (rc == NGX_AGAIN) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: nginx is asking us to wait for more data."); + ctx->waiting_more_body = 1; + return NGX_DONE; + } + } + + if (ctx->waiting_more_body == 0 && ctx->request_processed == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: request is ready to be processed."); + ngx_http_modsecurity_process_request(r); + ctx->request_processed = 1; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: returning NGX_OK. Count++ :P" ); + + return NGX_DECLINED; } +/* post read callback for rewrite and access phases */ +void +ngx_http_modsecurity_request_read(ngx_http_request_t *r) +{ + ngx_http_modsecurity_ctx_t *ctx; + + r->read_event_handler = ngx_http_request_empty_handler; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: reading request body..."); + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + + r->main->count--; + if (ctx->waiting_more_body) { + ctx->waiting_more_body = 0; + ngx_http_core_run_phases(r); + } + +} + +#ifndef NGX_HTTP_MODSECURITY_PREACCESS_HANDLER_ONLY static ngx_int_t ngx_http_modsecurity_header_filter(ngx_http_request_t *r) { ngx_http_modsecurity_loc_conf_t *cf; @@ -1102,8 +1164,8 @@ ngx_http_modsecurity_header_filter(ngx_http_request_t *r) { r->filter_need_in_memory = 1; return NGX_OK; } - - +#endif +#ifndef NGX_HTTP_MODSECURITY_PREACCESS_HANDLER_ONLY static ngx_int_t ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { @@ -1182,17 +1244,32 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) rc = move_brigade_to_chain(ctx->brigade, &out, r->pool); if (rc == NGX_ERROR) { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSec: Problems moving brigade to chain"); + return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR); } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: save headers out done: before"); + + if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK ||ngx_http_modsecurity_save_headers_out(r) != NGX_OK) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: save headers out done _inside_"); + + return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR); } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: save headers out done: done"); + + if (r->headers_out.content_length_n != -1) { r->headers_out.content_length_n = content_length; @@ -1200,16 +1277,18 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) } r->header_sent = 0; - rc = ngx_http_next_header_filter(r); + rc = ngx_http_next_header_filter(r); if (rc == NGX_ERROR || rc > NGX_OK) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: ngx_http_next_header_filter not NGX_OK."); + return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, rc); } return ngx_http_next_body_filter(r, out); } - -#define TXID_SIZE 25 +#endif static ngx_http_modsecurity_ctx_t * ngx_http_modsecurity_create_ctx(ngx_http_request_t *r) @@ -1232,6 +1311,7 @@ ngx_http_modsecurity_create_ctx(ngx_http_request_t *r) ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "modSecurity: ctx memory allocation error"); return NULL; } + cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_modsecurity_ctx_t)); if (cln == NULL) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "modSecurity: ctx memory allocation error"); @@ -1242,6 +1322,15 @@ ngx_http_modsecurity_create_ctx(ngx_http_request_t *r) ctx->r = r; + /* + + Using pcalloc already set to zero. + + ctx->request_processed = 0; + ctx->waiting_more_body = 0; + ctx->body_requested = 0; + */ + if (r->connection->requests == 0 || ctx->connection == NULL) { /* TODO: set server_rec, why igonre return value? */ @@ -1321,14 +1410,15 @@ ngx_http_modsecurity_create_ctx(ngx_http_request_t *r) if (ctx->brigade == NULL) { return NULL; } - return ctx; } - static void +static void ngx_http_modsecurity_cleanup(void *data) { - ngx_http_modsecurity_ctx_t *ctx = data; + ngx_http_modsecurity_ctx_t *ctx = data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "ModSec: Cleaning up: %p ", data); if (ctx->req != NULL) { (void) modsecFinishRequest(ctx->req); @@ -1336,7 +1426,7 @@ ngx_http_modsecurity_cleanup(void *data) if (ctx->connection != NULL) { (void) modsecFinishConnection(ctx->connection); } - + } static char * @@ -1372,7 +1462,6 @@ ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } - static char * ngx_http_modsecurity_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -1390,8 +1479,7 @@ ngx_http_modsecurity_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } - - static int +static int ngx_http_modsecurity_drop_action(request_rec *r) { ngx_http_modsecurity_ctx_t *ctx; diff --git a/tests/regression/misc/00-phases.t b/tests/regression/misc/00-phases.t index 2f1c4e59..b4c7e037 100644 --- a/tests/regression/misc/00-phases.t +++ b/tests/regression/misc/00-phases.t @@ -11,7 +11,7 @@ SecResponseBodyMimeType text/plain null SecRule REQUEST_LINE "^POST" "phase:1,pass,log,auditlog,id:500169" SecRule ARGS "val1" "phase:1,pass,log,auditlog,id:500170" - SecRule RESPONSE_HEADERS:Last-Modified "." "phase:1,pass,log,auditlog,id:500171" + SecRule RESPONSE_HEADERS:Last-Modified|RESPONSE_HEADERS:Content-type "." "phase:1,pass,log,auditlog,id:500171" SecRule RESPONSE_BODY "TEST" "phase:1,pass,log,auditlog,id:500172" ), match_log => { @@ -41,7 +41,7 @@ SecResponseBodyMimeType text/plain null SecRule REQUEST_LINE "^POST" "phase:2,pass,log,auditlog,id:500173" SecRule ARGS "val1" "phase:2,pass,log,auditlog,id:500174" - SecRule RESPONSE_HEADERS:Last-Modified "." "phase:2,pass,log,auditlog,id:500175" + SecRule RESPONSE_HEADERS:Last-Modified|RESPONSE_HEADERS:Content-type "." "phase:2,pass,log,auditlog,id:500175" SecRule RESPONSE_BODY "TEST" "phase:2,pass,log,auditlog,id:500176" ), match_log => { @@ -71,7 +71,7 @@ SecResponseBodyMimeType text/plain null SecRule REQUEST_LINE "^POST" "phase:3,pass,log,auditlog,id:500177" SecRule ARGS "val1" "phase:3,pass,log,auditlog,id:500178" - SecRule RESPONSE_HEADERS:Last-Modified "." "phase:3,pass,log,auditlog,id:500179" + SecRule RESPONSE_HEADERS:Last-Modified|RESPONSE_HEADERS:Content-type "." "phase:3,pass,log,auditlog,id:500179" SecRule RESPONSE_BODY "TEST" "phase:3,pass,log,auditlog,id:500180" ), match_log => { @@ -103,7 +103,7 @@ SecDebugLogLevel 9 SecRule REQUEST_LINE "^POST" "phase:4,pass,log,auditlog,id:500181" SecRule ARGS "val1" "phase:4,pass,log,auditlog,id:500182" - SecRule RESPONSE_HEADERS:Last-Modified "." "phase:4,pass,log,auditlog,id:500183" + SecRule RESPONSE_HEADERS:Last-Modified|RESPONSE_HEADERS:Content-Type "." "phase:4,pass,log,auditlog,id:500183" SecRule RESPONSE_BODY "TEST" "phase:4,pass,log,auditlog,id:500184" ), match_log => { @@ -132,7 +132,7 @@ SecResponseBodyMimeType text/plain null SecRule REQUEST_LINE "^POST" "phase:5,pass,log,auditlog,id:500185" SecRule ARGS "val1" "phase:5,pass,log,auditlog,id:500186" - SecRule RESPONSE_HEADERS:Last-Modified "." "phase:5,pass,log,auditlog,id:500187" + SecRule RESPONSE_HEADERS:Last-Modified|RESPONSE_HEADERS:Content-type "." "phase:5,pass,log,auditlog,id:500187" SecRule RESPONSE_BODY "TEST" "phase:5,pass,log,auditlog,id:500188" ), match_log => { diff --git a/tests/regression/misc/25-libinjection.t b/tests/regression/misc/25-libinjection.t index 28b133fd..b527beb8 100644 --- a/tests/regression/misc/25-libinjection.t +++ b/tests/regression/misc/25-libinjection.t @@ -19,7 +19,7 @@ status => qr/^403$/, }, request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html", + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", [ "Content-Type" => "application/x-www-form-urlencoded", ], @@ -46,7 +46,7 @@ status => qr/^200$/, }, request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html", + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", [ "Content-Type" => "application/x-www-form-urlencoded", ], @@ -73,7 +73,7 @@ status => qr/^403$/, }, request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html", + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", [ "Content-Type" => "application/x-www-form-urlencoded", ], @@ -100,7 +100,7 @@ status => qr/^200$/, }, request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html", + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", [ "Content-Type" => "application/x-www-form-urlencoded", ], diff --git a/tests/regression/nginx/conf/nginx.conf.template b/tests/regression/nginx/conf/nginx.conf.template index f5cb5e97..b6bf0054 100644 --- a/tests/regression/nginx/conf/nginx.conf.template +++ b/tests/regression/nginx/conf/nginx.conf.template @@ -1,8 +1,10 @@ -user root; -worker_processes 1; +worker_processes 1; daemon on; -error_log logs/error.log debug; +worker_rlimit_core 500M; +working_directory /tmp/; +error_log logs/error.log debug; + events { worker_connections 1024; } @@ -10,13 +12,42 @@ events { http { ModSecurityEnabled [% enable %]; ModSecurityConfig [% config %]; - server { + client_body_buffer_size 1024M; + server { + client_max_body_size 30M; listen [% listen %]; server_name localhost; - location / { - error_page 405 = $uri; + + + + location /no-proxy/test.txt { + echo "TEST"; } + + location /no-proxy/test2.txt { + echo "TEST 2"; + } + + location /proxy/test.txt { + proxy_pass http://localhost:[% listen %]/more/test.txt; + } + + location /proxy/test2.txt { + proxy_pass http://localhost:[% listen %]/more/test2.txt; + } + + location /test.txt { + echo "TEST"; + } + + location /test2.txt { + echo "TEST 2"; + } + + location / { + } + } }