Skip to content

Commit c752756

Browse files
committed
Hook into Nginx NGX_HTTP_ACCESS_PHASE instead of NGX_HTTP_PREACCESS_PHASE
1 parent fd28e6a commit c752756

File tree

6 files changed

+282
-273
lines changed

6 files changed

+282
-273
lines changed

config

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,10 @@ if test -n "$ngx_module_link"; then
117117
ngx_module_type=HTTP_FILTER
118118
ngx_module_name="$ngx_addon_name"
119119
ngx_module_srcs="$ngx_addon_dir/src/ngx_http_modsecurity_module.c \
120-
$ngx_addon_dir/src/ngx_http_modsecurity_pre_access.c \
120+
$ngx_addon_dir/src/ngx_http_modsecurity_access.c \
121121
$ngx_addon_dir/src/ngx_http_modsecurity_header_filter.c \
122122
$ngx_addon_dir/src/ngx_http_modsecurity_body_filter.c \
123123
$ngx_addon_dir/src/ngx_http_modsecurity_log.c \
124-
$ngx_addon_dir/src/ngx_http_modsecurity_rewrite.c \
125124
"
126125
ngx_module_deps="$ngx_addon_dir/src/ddebug.h \
127126
$ngx_addon_dir/src/ngx_http_modsecurity_common.h \
@@ -148,11 +147,10 @@ else
148147
NGX_ADDON_SRCS="\
149148
$NGX_ADDON_SRCS \
150149
$ngx_addon_dir/src/ngx_http_modsecurity_module.c \
151-
$ngx_addon_dir/src/ngx_http_modsecurity_pre_access.c \
150+
$ngx_addon_dir/src/ngx_http_modsecurity_access.c \
152151
$ngx_addon_dir/src/ngx_http_modsecurity_header_filter.c \
153152
$ngx_addon_dir/src/ngx_http_modsecurity_body_filter.c \
154153
$ngx_addon_dir/src/ngx_http_modsecurity_log.c \
155-
$ngx_addon_dir/src/ngx_http_modsecurity_rewrite.c \
156154
"
157155

158156
NGX_ADDON_DEPS="\

src/ngx_http_modsecurity_rewrite.c renamed to src/ngx_http_modsecurity_access.c

Lines changed: 198 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,30 @@
2222

2323
#include "ngx_http_modsecurity_common.h"
2424

25+
void
26+
ngx_http_modsecurity_request_read(ngx_http_request_t *r)
27+
{
28+
ngx_http_modsecurity_ctx_t *ctx;
29+
30+
ctx = ngx_http_modsecurity_get_module_ctx(r);
31+
32+
#if defined(nginx_version) && nginx_version >= 8011
33+
r->main->count--;
34+
#endif
35+
36+
if (ctx->waiting_more_body)
37+
{
38+
ctx->waiting_more_body = 0;
39+
r->write_event_handler = ngx_http_core_run_phases;
40+
ngx_http_core_run_phases(r);
41+
}
42+
}
43+
44+
2545
ngx_int_t
26-
ngx_http_modsecurity_rewrite_handler(ngx_http_request_t *r)
46+
ngx_http_modsecurity_access_handler(ngx_http_request_t *r)
2747
{
48+
2849
ngx_pool_t *old_pool;
2950
ngx_http_modsecurity_ctx_t *ctx;
3051
ngx_http_modsecurity_conf_t *mcf;
@@ -44,7 +65,7 @@ ngx_http_modsecurity_rewrite_handler(ngx_http_request_t *r)
4465
}
4566
*/
4667

47-
dd("catching a new _rewrite_ phase handler");
68+
dd("catching a new _access_ phase handler");
4869

4970
ctx = ngx_http_modsecurity_get_module_ctx(r);
5071

@@ -264,6 +285,181 @@ ngx_http_modsecurity_rewrite_handler(ngx_http_request_t *r)
264285
}
265286
}
266287

288+
#if 1
289+
290+
/*
291+
* FIXME:
292+
* In order to perform some tests, let's accept everything.
293+
*
294+
if (r->method != NGX_HTTP_GET &&
295+
r->method != NGX_HTTP_POST && r->method != NGX_HTTP_HEAD) {
296+
dd("ModSecurity is not ready to deal with anything different from " \
297+
"POST, GET or HEAD");
298+
return NGX_DECLINED;
299+
}
300+
*/
301+
302+
ctx = ngx_http_modsecurity_get_module_ctx(r);
303+
304+
dd("recovering ctx: %p", ctx);
267305

306+
if (ctx == NULL)
307+
{
308+
dd("ctx is null; Nothing we can do, returning an error.");
309+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
310+
}
311+
312+
if (ctx->request_body_processed) {
313+
// should we use r->internal or r->filter_finalize?
314+
return NGX_DECLINED;
315+
}
316+
317+
if (ctx->intervention_triggered) {
318+
return NGX_DECLINED;
319+
}
320+
321+
if (ctx->waiting_more_body == 1)
322+
{
323+
dd("waiting for more data before proceed. / count: %d",
324+
r->main->count);
325+
326+
return NGX_DONE;
327+
}
328+
329+
if (ctx->body_requested == 0)
330+
{
331+
ngx_int_t rc = NGX_OK;
332+
333+
ctx->body_requested = 1;
334+
335+
dd("asking for the request body, if any. Count: %d",
336+
r->main->count);
337+
/**
338+
* TODO: Check if there is any benefit to use request_body_in_single_buf set to 1.
339+
*
340+
* saw some module using this request_body_in_single_buf
341+
* but not sure what exactly it does, same for the others options below.
342+
*
343+
* r->request_body_in_single_buf = 1;
344+
*/
345+
r->request_body_in_single_buf = 1;
346+
r->request_body_in_persistent_file = 1;
347+
if (!r->request_body_in_file_only) {
348+
// If the above condition fails, then the flag below will have been
349+
// set correctly elsewhere. We need to set the flag here for other
350+
// conditions (client_body_in_file_only not used but
351+
// client_body_buffer_size is)
352+
r->request_body_in_clean_file = 1;
353+
}
354+
355+
rc = ngx_http_read_client_request_body(r,
356+
ngx_http_modsecurity_request_read);
357+
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
358+
#if (nginx_version < 1002006) || \
359+
(nginx_version >= 1003000 && nginx_version < 1003009)
360+
r->main->count--;
361+
#endif
362+
363+
return rc;
364+
}
365+
if (rc == NGX_AGAIN)
366+
{
367+
dd("nginx is asking us to wait for more data.");
368+
369+
ctx->waiting_more_body = 1;
370+
return NGX_DONE;
371+
}
372+
}
373+
374+
if (ctx->waiting_more_body == 0)
375+
{
376+
int ret = 0;
377+
int already_inspected = 0;
378+
379+
dd("request body is ready to be processed");
380+
381+
r->write_event_handler = ngx_http_core_run_phases;
382+
383+
ngx_chain_t *chain = r->request_body->bufs;
384+
385+
/**
386+
* TODO: Speed up the analysis by sending chunk while they arrive.
387+
*
388+
* Notice that we are waiting for the full request body to
389+
* start to process it, it may not be necessary. We may send
390+
* the chunks to ModSecurity while nginx keep calling this
391+
* function.
392+
*/
393+
394+
if (r->request_body->temp_file != NULL) {
395+
ngx_str_t file_path = r->request_body->temp_file->file.name;
396+
const char *file_name = ngx_str_to_char(file_path, r->pool);
397+
if (file_name == (char*)-1) {
398+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
399+
}
400+
/*
401+
* Request body was saved to a file, probably we don't have a
402+
* copy of it in memory.
403+
*/
404+
dd("request body inspection: file -- %s", file_name);
405+
406+
msc_request_body_from_file(ctx->modsec_transaction, file_name);
407+
408+
already_inspected = 1;
409+
} else {
410+
dd("inspection request body in memory.");
411+
}
412+
413+
while (chain && !already_inspected)
414+
{
415+
u_char *data = chain->buf->pos;
416+
417+
msc_append_request_body(ctx->modsec_transaction, data,
418+
chain->buf->last - data);
419+
420+
if (chain->buf->last_buf) {
421+
break;
422+
}
423+
chain = chain->next;
424+
425+
/* XXX: chains are processed one-by-one, maybe worth to pass all chains and then call intervention() ? */
426+
427+
/**
428+
* ModSecurity may perform stream inspection on this buffer,
429+
* it may ask for a intervention in consequence of that.
430+
*
431+
*/
432+
ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r, 0);
433+
if (ret > 0) {
434+
return ret;
435+
}
436+
}
437+
438+
/**
439+
* At this point, all the request body was sent to ModSecurity
440+
* and we want to make sure that all the request body inspection
441+
* happened; consequently we have to check if ModSecurity have
442+
* returned any kind of intervention.
443+
*/
444+
445+
/* XXX: once more -- is body can be modified ? content-length need to be adjusted ? */
446+
447+
old_pool = ngx_http_modsecurity_pcre_malloc_init(r->pool);
448+
msc_process_request_body(ctx->modsec_transaction);
449+
ctx->request_body_processed = 1;
450+
ngx_http_modsecurity_pcre_malloc_done(old_pool);
451+
452+
ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r, 0);
453+
if (r->error_page) {
454+
return NGX_DECLINED;
455+
}
456+
if (ret > 0) {
457+
return ret;
458+
}
459+
}
460+
461+
dd("Nothing to add on the body inspection, reclaiming a NGX_DECLINED");
462+
#endif
268463
return NGX_DECLINED;
269464
}
465+

src/ngx_http_modsecurity_common.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,7 @@ int ngx_http_modsecurity_store_ctx_header(ngx_http_request_t *r, ngx_str_t *name
165165
void ngx_http_modsecurity_log(void *log, const void* data);
166166
ngx_int_t ngx_http_modsecurity_log_handler(ngx_http_request_t *r);
167167

168-
/* ngx_http_modsecurity_pre_access.c */
169-
ngx_int_t ngx_http_modsecurity_pre_access_handler(ngx_http_request_t *r);
170-
171-
/* ngx_http_modsecurity_rewrite.c */
172-
ngx_int_t ngx_http_modsecurity_rewrite_handler(ngx_http_request_t *r);
173-
168+
/* ngx_http_modsecurity_access.c */
169+
ngx_int_t ngx_http_modsecurity_access_handler(ngx_http_request_t *r);
174170

175171
#endif /* _NGX_HTTP_MODSECURITY_COMMON_H_INCLUDED_ */

src/ngx_http_modsecurity_module.c

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,7 @@ ngx_module_t ngx_http_modsecurity_module = {
551551
static ngx_int_t
552552
ngx_http_modsecurity_init(ngx_conf_t *cf)
553553
{
554-
ngx_http_handler_pt *h_rewrite;
555-
ngx_http_handler_pt *h_preaccess;
554+
ngx_http_handler_pt *h_access;
556555
ngx_http_handler_pt *h_log;
557556
ngx_http_core_main_conf_t *cmcf;
558557
int rc = 0;
@@ -565,35 +564,19 @@ ngx_http_modsecurity_init(ngx_conf_t *cf)
565564
}
566565
/**
567566
*
568-
* Seems like we cannot do this very same thing with
569-
* NGX_HTTP_FIND_CONFIG_PHASE. it does not seems to
570-
* be an array. Our next option is the REWRITE.
571-
*
572-
* TODO: check if we can hook prior to NGX_HTTP_REWRITE_PHASE phase.
567+
* We want to process everything in the NGX_HTTP_ACCESS_PHASE because we need to allow
568+
* ngx_http_limit_*_module to run
573569
*
574570
*/
575-
h_rewrite = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);
576-
if (h_rewrite == NULL)
577-
{
578-
dd("Not able to create a new NGX_HTTP_REWRITE_PHASE handle");
579-
return NGX_ERROR;
580-
}
581-
*h_rewrite = ngx_http_modsecurity_rewrite_handler;
582571

583-
/**
584-
*
585-
* Processing the request body on the preaccess phase.
586-
*
587-
* TODO: check if hook into separated phases is the best thing to do.
588-
*
589-
*/
590-
h_preaccess = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
591-
if (h_preaccess == NULL)
572+
h_access = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
573+
if (h_access == NULL)
592574
{
593-
dd("Not able to create a new NGX_HTTP_PREACCESS_PHASE handle");
575+
dd("Not able to create a new NGX_HTTP_ACCESS_PHASE handle");
594576
return NGX_ERROR;
595577
}
596-
*h_preaccess = ngx_http_modsecurity_pre_access_handler;
578+
*h_access = ngx_http_modsecurity_access_handler;
579+
597580

598581
/**
599582
* Process the log phase.

0 commit comments

Comments
 (0)