diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index a6cd4b578c..6aab9e3240 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -48,13 +48,10 @@ typedef struct ngx_http_modsecurity_ctx_s { conn_rec *connection; request_rec *req; ngx_chain_t *chain; - ngx_chain_t *last; - /* used in modSecurity body handler */ - ssize_t received; - ssize_t processed; - ngx_chain_t *body_last; - u_char *body_pos; - ngx_http_request_body_data_handler_pt data_handler; + ngx_buf_t buf; + void **loc_conf; + unsigned request_body_in_single_buf:1; + unsigned request_body_in_file_only:1; } ngx_http_modsecurity_ctx_t; @@ -62,6 +59,7 @@ typedef struct ngx_http_modsecurity_ctx_s { ** Module's registred function/handlers. */ static ngx_int_t ngx_http_modsecurity_handler(ngx_http_request_t *r); +static void ngx_http_modsecurity_request_body_handler(ngx_http_request_t *r); //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_exit_process(ngx_cycle_t *cycle); @@ -71,18 +69,14 @@ static char *ngx_http_modsecurity_set_config(ngx_conf_t *cf, ngx_command_t *cmd, apr_status_t modsecurity_read_body_cb(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos); -static ngx_int_t ngx_http_process_request_body(ngx_http_request_t *r, ngx_chain_t *body); -ngx_int_t ngx_http_read_upload_client_request_body(ngx_http_request_t *r); -static void ngx_http_read_upload_client_request_body_handler(ngx_http_request_t *r); -static ngx_int_t upload_process_buf(ngx_http_modsecurity_ctx_t *ctx, u_char *start, u_char *end); -static ngx_int_t ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r); -static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r); static char *ngx_http_modsecurity_add_handler(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_modsecurity_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_modsecurity_pass_to_backend(ngx_http_request_t *r); static int ngx_http_modsecurity_drop_action(request_rec *r); +static void ngx_http_modsecurity_cleanup(void *data); + /* command handled by the module */ static ngx_command_t ngx_http_modsecurity_commands[] = { { ngx_string("ModSecurityConfig"), @@ -226,6 +220,7 @@ ngx_http_modsecurity_init_process(ngx_cycle_t *cycle) modsecSetLogHook(cycle->log, modsecLog); modsecSetDropAction(ngx_http_modsecurity_drop_action); + modsecSetReadBody(modsecurity_read_body_cb); modsecInit(); /* config was already parsed in master process */ @@ -270,358 +265,6 @@ ConvertNgxStringToUTF8(ngx_str_t str, apr_pool_t *pool) return t; } -ngx_int_t -ngx_http_read_upload_client_request_body(ngx_http_request_t *r) { - ssize_t size, preread; - ngx_buf_t *b; - ngx_chain_t *cl, **next; - ngx_http_request_body_t *rb; - ngx_http_core_loc_conf_t *clcf; - ngx_http_modsecurity_ctx_t *ctx; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: ngx_http_read_upload_client_request_body"); - -#if defined nginx_version && nginx_version >= 8011 - r->main->count++; -#endif - - if (r->request_body || r->discard_body) { - return NGX_OK; - } - - rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); - if (rb == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - r->request_body = rb; - - if (r->headers_in.content_length_n <= 0) { - return NGX_HTTP_BAD_REQUEST; - } - - ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); - if (ctx == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - /* - * set by ngx_pcalloc(): - * - * rb->bufs = NULL; - * rb->buf = NULL; - * rb->rest = 0; - */ - - preread = r->header_in->last - r->header_in->pos; - - if (preread) { - - /* there is the pre-read part of the request body */ - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "modSecurity: http client request body preread %uz", preread); - - ctx->received = preread; - - b = ngx_calloc_buf(r->pool); - if (b == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - b->temporary = 1; - b->start = r->header_in->pos; - b->pos = r->header_in->pos; - b->last = r->header_in->last; - b->end = r->header_in->end; - - rb->bufs = ngx_alloc_chain_link(r->pool); - if (rb->bufs == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - rb->bufs->buf = b; - rb->bufs->next = NULL; - rb->buf = b; - - if (preread >= r->headers_in.content_length_n) { - - /* the whole request body was pre-read */ - - r->header_in->pos += r->headers_in.content_length_n; - r->request_length += r->headers_in.content_length_n; - - if (ngx_http_process_request_body(r, rb->bufs) != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - return ngx_http_upload_body_handler(r); - } - - /* - * to not consider the body as pipelined request in - * ngx_http_set_keepalive() - */ - r->header_in->pos = r->header_in->last; - - r->request_length += preread; - - rb->rest = r->headers_in.content_length_n - preread; - - if (rb->rest <= (off_t) (b->end - b->last)) { - - /* the whole request body may be placed in r->header_in */ - - rb->to_write = rb->bufs; - - r->read_event_handler = ngx_http_read_upload_client_request_body_handler; - - return ngx_http_do_read_upload_client_request_body(r); - } - - next = &rb->bufs->next; - - } else { - b = NULL; - rb->rest = r->headers_in.content_length_n; - next = &rb->bufs; - } - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - size = clcf->client_body_buffer_size; - size += size >> 2; - - if (rb->rest < (ssize_t) size) { - size = rb->rest; - - if (r->request_body_in_single_buf) { - size += preread; - } - - } else { - size = clcf->client_body_buffer_size; - - /* disable copying buffer for r->request_body_in_single_buf */ - b = NULL; - } - - rb->buf = ngx_create_temp_buf(r->pool, size); - if (rb->buf == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - cl->buf = rb->buf; - cl->next = NULL; - - if (b && r->request_body_in_single_buf) { - size = b->last - b->pos; - ngx_memcpy(rb->buf->pos, b->pos, size); - rb->buf->last += size; - - next = &rb->bufs; - } - - *next = cl; - - rb->to_write = rb->bufs; - - r->read_event_handler = ngx_http_read_upload_client_request_body_handler; - - return ngx_http_do_read_upload_client_request_body(r); -} - -static void -ngx_http_read_upload_client_request_body_handler(ngx_http_request_t *r) -{ - ngx_int_t rc; - ngx_event_t *rev = r->connection->read; - ngx_http_core_loc_conf_t *clcf; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: ngx_http_read_upload_client_request_body_handler"); - - if (rev->timedout) { - rev->timedout = 0; - rev->delayed = 0; - - if (!rev->ready) { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ngx_add_timer(rev, clcf->client_body_timeout); - - if (ngx_handle_read_event(rev, clcf->send_lowat) != NGX_OK) { - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - return; - } - } - - rc = ngx_http_do_read_upload_client_request_body(r); - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: ngx_http_read_upload_client_request_body_handler, finalizing"); - ngx_http_finalize_request(r, rc); - } -} - - -static ngx_int_t -ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r) -{ - ssize_t size, n; - ngx_connection_t *c; - ngx_http_request_body_t *rb; - ngx_int_t rc; - ngx_http_core_loc_conf_t *clcf; - ngx_http_modsecurity_ctx_t *ctx; - - c = r->connection; - rb = r->request_body; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "modSecurity: http read client request body"); - - ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); - if (ctx == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - for ( ;; ) { - for ( ;; ) { - if (rb->buf->last == rb->buf->end) { - - rc = ngx_http_process_request_body(r, rb->to_write); - if(rc != NGX_OK) { - return rc; - } - - rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; - rb->buf->last = rb->buf->start; - } - - size = rb->buf->end - rb->buf->last; - - if ((off_t)size > rb->rest) { - size = (size_t)rb->rest; - } - - n = c->recv(c, rb->buf->last, size); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "modSecurity: http client request body recv %z", n); - - if (n == NGX_AGAIN) { - break; - } - - if (n == 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "modSecurity: client closed prematurely connection"); - } - - if (n == 0 || n == NGX_ERROR) { - c->error = 1; - return NGX_HTTP_BAD_REQUEST; - } - - rb->buf->last += n; - rb->rest -= n; - r->request_length += n; - ctx->received += n; - - if (rb->rest == 0) { - break; - } - - if (rb->buf->last < rb->buf->end) { - break; - } - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "modSecurity: http client request body rest %uz", rb->rest); - - if (rb->rest == 0) { - break; - } - - if (!c->read->ready) { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ngx_add_timer(c->read, clcf->client_body_timeout); - - if (ngx_handle_read_event(c->read, 0) != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - return NGX_AGAIN; - } - } - - if (c->read->timer_set) { - ngx_del_timer(c->read); - } - - rc = ngx_http_process_request_body(r, rb->to_write); - if(rc != NGX_OK) { - return rc; - } - - return ngx_http_upload_body_handler(r); -} - -static ngx_int_t -ngx_http_process_request_body(ngx_http_request_t *r, ngx_chain_t *body) -{ - ngx_int_t rc; - ngx_http_modsecurity_ctx_t *ctx; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "modSecurity: ngx_http_process_request_body"); - - ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); - if (ctx == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - /* Feed all the buffers into data handler */ - while (body) { - rc = ctx->data_handler(ctx, body->buf->pos, body->buf->last); - - if(rc != NGX_OK) - return rc; - - body = body->next; - } - - /* Signal end of body */ - if (r->request_body->rest == 0) { - rc = ctx->data_handler(ctx, NULL, NULL); - - if(rc != NGX_OK) - return rc; - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_upload_body_handler(ngx_http_request_t *r) -{ - ngx_int_t rc; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "modSecurity: ngx_http_upload_body_handler"); - - rc = ngx_http_modsecurity_pass_to_backend(r); - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - - return NGX_OK; -} - static ngx_int_t ngx_http_modsecurity_pass_to_backend(ngx_http_request_t *r) @@ -665,17 +308,6 @@ ngx_http_modsecurity_pass_to_backend(ngx_http_request_t *r) args = r->args; /* forward the query args */ flags = 0; - /* XXX: this looks ugly, should we process PUT also? */ - if (r->method == NGX_HTTP_POST && r->request_body) { - r->request_body->bufs = ctx->chain; - /* do we really need it ? :) */ - r->read_event_handler = ngx_http_request_empty_handler; -#if defined nginx_version && nginx_version >= 8011 - r->main->count--; -#endif - - } - if (cf->url_cv) { /* complex value */ if (ngx_http_complex_value(r, cf->url_cv, &uri) != NGX_OK) { @@ -711,57 +343,16 @@ ngx_http_modsecurity_pass_to_backend(ngx_http_request_t *r) return rc; } - -static ngx_int_t -upload_process_buf(ngx_http_modsecurity_ctx_t *ctx, u_char *start, u_char *end) -{ - ngx_http_request_t *r = ctx->r; - ngx_buf_t *b; - ngx_chain_t *cl; - - /* No more data? */ - if (start == end) { - return NGX_OK; /* confirm end of stream */ - } - - b = ngx_create_temp_buf(r->pool, (size_t)(end - start)); - if (b == NULL) { - return NGX_ERROR; - } - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NGX_ERROR; - } - - b->last_in_chain = 0; - - cl->buf = b; - cl->next = NULL; - - b->last = ngx_cpymem(b->last, start, (size_t)(end - start)); - - if (ctx->chain == NULL) { - ctx->chain = cl; - ctx->last = cl; - } else { - ctx->last->next = cl; - ctx->last = cl; - } - - return NGX_OK; -} - /* ** request body callback, passing body to mod security */ apr_status_t -modsecurity_read_body_cb(request_rec *r, char *buf, unsigned int length, - unsigned int *readcnt, int *is_eos) +modsecurity_read_body_cb(request_rec *r, char *outpos, unsigned int length, + unsigned int *outlen, int *is_eos) { - ngx_chain_t *body; + size_t len, rest; + ssize_t size; ngx_http_modsecurity_ctx_t *ctx; - ngx_buf_t *b; ctx = (ngx_http_modsecurity_ctx_t *) apr_table_get(r->notes, NOTE_NGINX_REQUEST_CTX); if (ctx == NULL) { @@ -769,41 +360,47 @@ modsecurity_read_body_cb(request_rec *r, char *buf, unsigned int length, } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "modSecurity: read_body_cb"); - if (ctx->processed >= ctx->received) { - *is_eos = 1; - return APR_SUCCESS; - } - - if (ctx->body_last == NULL) { - body = ctx->chain; - } else { - body = ctx->body_last; - } - - if (!body) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "modSecurity: no more body left"); - } + ngx_buf_t *buf = &ctx->buf; + rest = length; + *is_eos = 0; + + while (rest) { + + if (ngx_buf_size(buf) == 0) { + if (ctx->chain == NULL) { + *outlen = length - rest; + *is_eos = 1; + // END + return APR_SUCCESS; + } - if (body) { - b = body->buf; - if (!ctx->body_pos) { - ctx->body_pos = b->start; + ngx_memcpy(buf, ctx->chain->buf, sizeof(ngx_buf_t)); + ctx->chain = ctx->chain->next; } - if ((unsigned int)(b->end - ctx->body_pos) > length) { - ngx_memcpy(buf, (char *) ctx->body_pos, length); - ctx->processed += length; - ctx->body_pos += length; - *readcnt = length; - ctx->body_last = body; + + len = (size_t) ngx_min((size_t)ngx_buf_size(buf), rest); + + if (ngx_buf_in_memory(buf)) { + + outpos = (char *) ngx_cpymem(outpos, buf->pos, len); + rest -= len; + buf->pos += len; + } else if (buf->in_file) { + + size = ngx_read_file(buf->file, (u_char*)outpos, len, buf->file_pos); + + if (size < 0) { + return NGX_ERROR; + } + outpos += size; + rest -= size; + buf->file_pos += size; } else { - ngx_memcpy(buf, (char *) ctx->body_pos, (b->end - ctx->body_pos)); - ctx->processed += (b->end - ctx->body_pos); - *readcnt = (b->end - ctx->body_pos); - ctx->body_last = body->next; - ctx->body_pos = NULL; + return -1; } } - + + *outlen = length - rest; return APR_SUCCESS; } @@ -849,11 +446,14 @@ 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_list_part_t *part; - ngx_table_elt_t *h; - ngx_uint_t i; - ngx_int_t rc; + ngx_http_core_loc_conf_t *clcf, *lcf; + ngx_http_modsecurity_ctx_t *ctx; + ngx_list_part_t *part; + ngx_table_elt_t *h; + ngx_uint_t i; + ngx_int_t rc; + void **loc_conf; + ngx_pool_cleanup_t *cln; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: handler"); @@ -879,11 +479,6 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) return NGX_ERROR; } ctx->r = r; - ctx->data_handler = upload_process_buf; - ctx->chain = ctx->last = NULL; - ctx->body_last = NULL; - ctx->body_pos = NULL; - ctx->received = ctx->processed = 0; ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); } @@ -946,28 +541,113 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) apr_table_setn(ctx->req->subprocess_env, "UNIQUE_ID", "12345"); /* actually, we need ctx only for POST request body handling - don't like this part */ apr_table_setn(ctx->req->notes, NOTE_NGINX_REQUEST_CTX, (const char *) ctx); + + /* add cleanup */ + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + cln->data = ctx; + cln->handler = ngx_http_modsecurity_cleanup; } // r->keepalive = 0; if (r->method == NGX_HTTP_POST) { /* Processing POST request body, should we process PUT? */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: method POST"); - if (cf->enable) - modsecSetReadBody(modsecurity_read_body_cb); - rc = ngx_http_read_upload_client_request_body(r); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx->loc_conf = r->loc_conf; + /* hijack loc_conf so that we can receive any body length + * TODO: nonblocking process & chuncked body + */ + if (clcf->client_body_buffer_size < r->headers_in.content_length_n) { + + loc_conf = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); + if (loc_conf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + lcf = ngx_pcalloc(r->pool, sizeof(ngx_http_core_loc_conf_t)); + if (lcf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_memcpy(loc_conf, r->loc_conf, sizeof(void *) * ngx_http_max_module); + ngx_memcpy(lcf, clcf, sizeof(ngx_http_core_loc_conf_t)); + + ctx->loc_conf = r->loc_conf; + r->loc_conf = loc_conf; + + ngx_http_get_module_loc_conf(r, ngx_http_core_module) = lcf; + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + clcf->client_body_buffer_size = r->headers_in.content_length_n; + } + + ctx->request_body_in_single_buf = r->request_body_in_single_buf; + ctx->request_body_in_file_only = r->request_body_in_file_only; + r->request_body_in_single_buf = 1; + r->request_body_in_file_only = 0; + + rc = ngx_http_read_client_request_body(r, ngx_http_modsecurity_request_body_handler); + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_DONE; + } else { /* processing all the other methods */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: method is not POST"); -/* rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - }*/ + rc = ngx_http_modsecurity_pass_to_backend(r); } return rc; } +static void +ngx_http_modsecurity_cleanup(void *data) +{ + ngx_http_modsecurity_ctx_t *ctx = data; + + if (ctx->req != NULL) { + (void) modsecFinishRequest(ctx->req); + } +} + + +static void +ngx_http_modsecurity_request_body_handler(ngx_http_request_t *r) +{ + ngx_http_modsecurity_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + + if (ctx == NULL + || r->request_body->bufs == NULL + || r->request_body->bufs->next != NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + r->request_body_in_single_buf = ctx->request_body_in_single_buf; + r->request_body_in_file_only = ctx->request_body_in_file_only; + r->header_in = r->request_body->bufs->buf; + ctx->chain = r->request_body->bufs; + r->request_body = NULL; + r->loc_conf = ctx->loc_conf; + + ngx_http_finalize_request(r, ngx_http_modsecurity_pass_to_backend(r)); + return; +} + + static char * ngx_http_modsecurity_set_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {