diff --git a/config b/config index ce77a3e..c6e7467 100644 --- a/config +++ b/config @@ -82,6 +82,30 @@ fi ngx_addon_name=ngx_http_modsecurity_module +# We must place ngx_http_modsecurity_module after ngx_http_gzip_filter_module +# in load order list to be able to read response body before it gets compressed +# (for filter modules later initialization means earlier execution). +# +# Nginx implements load ordering only for dynamic modules and only a BEFORE part +# of "ngx_module_order". So we list all of the modules that come after +# ngx_http_gzip_filter_module as a BEFORE dependency for +# ngx_http_modsecurity_module. +# +# For static compilation HTTP_FILTER_MODULES will be patched later. + +modsecurity_dependency="ngx_http_postpone_filter_module \ + ngx_http_ssi_filter_module \ + ngx_http_charset_filter_module \ + ngx_http_xslt_filter_module \ + ngx_http_image_filter_module \ + ngx_http_sub_filter_module \ + ngx_http_addition_filter_module \ + ngx_http_gunzip_filter_module \ + ngx_http_userid_filter_module \ + ngx_http_headers_filter_module \ + ngx_http_copy_filter_module" + + if test -n "$ngx_module_link"; then ngx_module_type=HTTP_FILTER ngx_module_name="$ngx_addon_name" @@ -98,7 +122,12 @@ if test -n "$ngx_module_link"; then ngx_module_libs="$ngx_feature_libs" ngx_module_incs="$ngx_feature_path" - ngx_module_order="ngx_http_chunked_filter_module ngx_http_v2_filter_module $ngx_module_name ngx_http_range_header_filter_module" + ngx_module_order="ngx_http_chunked_filter_module \ + ngx_http_v2_filter_module \ + ngx_http_range_header_filter_module \ + ngx_http_gzip_filter_module \ + $ngx_module_name \ + $modsecurity_dependency"; . auto/module else @@ -128,20 +157,36 @@ fi # # Nginx does not provide reliable way to introduce our module into required -# place in static ($ngx_module_link=ADDON) compilation mode, so we should +# place in static ($ngx_module_link=ADDON) compilation mode, so we must # explicitly update module "ordering rules". # -# Default runtime location of ngx_http_modsecurity_module is right before -# ngx_http_chunked_filter_module, but in case if ngx_http_v2_filter_module is -# compiled in, we should put our module before ngx_http_v2_filter_module in -# order to support SecRules processing for HTTP/2.0 requests. -# if [ "$ngx_module_link" != DYNAMIC ] ; then - pre_module='ngx_http_chunked_filter_module' - if [ "$HTTP_V2" = "YES" ]; then - pre_module='ngx_http_v2_filter_module' + # Reposition modsecurity module to satisfy $modsecurity_dependency + # (this mimics dependency resolution made by ngx_add_module() function + # though less optimal in terms of computational complexity). + modules= + found= + for module in $HTTP_FILTER_MODULES; do + # skip our module name from the original list + if [ "$module" = "$ngx_addon_name" ]; then + continue + fi + if [ -z "${found}" ]; then + for item in $modsecurity_dependency; do + if [ "$module" = "$item" ]; then + modules="${modules} $ngx_addon_name" + found=1 + break + fi + done + fi + modules="${modules} $module" + done + if [ -z "${found}" ]; then + # This must never happen since ngx_http_copy_filter_module must be in HTTP_FILTER_MODULES + # and we stated dependency on it in $modsecurity_dependency + echo "$0: error: cannot reposition modsecurity module in HTTP_FILTER_MODULES list" + exit 1 fi - HTTP_FILTER_MODULES=`echo $HTTP_FILTER_MODULES | \ - sed -E "s/$ngx_addon_name/ /g" | \ - sed -E "s/$pre_module/$pre_module $ngx_addon_name/g"` + HTTP_FILTER_MODULES="${modules}" fi diff --git a/src/ngx_http_modsecurity_body_filter.c b/src/ngx_http_modsecurity_body_filter.c index f8b3c71..fd286d0 100644 --- a/src/ngx_http_modsecurity_body_filter.c +++ b/src/ngx_http_modsecurity_body_filter.c @@ -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) @@ -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->start; + 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->end - 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); } diff --git a/src/ngx_http_modsecurity_pre_access.c b/src/ngx_http_modsecurity_pre_access.c index 70f9feb..6f4cbcb 100644 --- a/src/ngx_http_modsecurity_pre_access.c +++ b/src/ngx_http_modsecurity_pre_access.c @@ -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;