diff --git a/nginx/modsecurity/apr_bucket_nginx.c b/nginx/modsecurity/apr_bucket_nginx.c new file mode 100644 index 0000000000..d9b0155486 --- /dev/null +++ b/nginx/modsecurity/apr_bucket_nginx.c @@ -0,0 +1,232 @@ + +#include + +static apr_status_t nginx_bucket_read(apr_bucket *b, const char **str, + apr_size_t *len, apr_read_type_e block); +static void nginx_bucket_destroy(void *data); + +static const apr_bucket_type_t apr_bucket_type_nginx = { + "NGINX", 5, APR_BUCKET_DATA, + nginx_bucket_destroy, + nginx_bucket_read, + apr_bucket_setaside_noop, + apr_bucket_shared_split, + apr_bucket_shared_copy +}; + + +typedef struct apr_bucket_nginx { + apr_bucket_refcount refcount; + ngx_buf_t *buf; +} apr_bucket_nginx; + +/* ngx_buf_t to apr_bucket */ +apr_bucket * apr_bucket_nginx_create(ngx_buf_t *buf, + apr_pool_t *p, + apr_bucket_alloc_t *list) +{ + + apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); + + APR_BUCKET_INIT(b); /* link */ + b->free = apr_bucket_free; + b->list = list; + return apr_bucket_nginx_make(b, buf, p); +} + +apr_bucket * apr_bucket_nginx_make(apr_bucket *b, ngx_buf_t *buf, + apr_pool_t *pool) +{ + apr_bucket_nginx *n; + + n = apr_bucket_alloc(sizeof(*n), b->list); + + n->buf = buf; + + b = apr_bucket_shared_make(b, n, 0, ngx_buf_size(buf)); + b->type = &apr_bucket_type_nginx; + return b; +} + +static apr_status_t nginx_bucket_read(apr_bucket *b, const char **str, + apr_size_t *len, apr_read_type_e block) +{ + apr_bucket_nginx *n = b->data; + ngx_buf_t *buf = n->buf; + u_char *data; + ssize_t size; + + if (buf->pos == NULL && ngx_buf_size(buf) != 0) { + data = apr_bucket_alloc(ngx_buf_size(buf), b->list); + if (data == NULL) { + return APR_EGENERAL; + } + + size = ngx_read_file(buf->file, data, ngx_buf_size(buf), buf->file_pos); + if (size != ngx_buf_size(buf)) { + apr_bucket_free(data); + return APR_EGENERAL; + } + buf->pos = data; + } + + *str = (char *)buf->pos + b->start; + *len = b->length; + + return APR_SUCCESS; +} + + +static void nginx_bucket_destroy(void *data) +{ + apr_bucket_nginx *n = data; + ngx_buf_t *buf = n->buf; + + if (apr_bucket_shared_destroy(n)) { + if (!ngx_buf_in_memory(buf) && buf->pos != NULL) { + apr_bucket_free(buf->pos); + buf->pos = NULL; + } + apr_bucket_free(n); + } +} + +ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool) { + ngx_buf_t *buf, *b; + apr_bucket_nginx *n; + ngx_uint_t len; + u_char *data; + + if (e->type->is_metadata) { + return NULL; + } + + if (e->type == &apr_bucket_type_nginx) { + n = e->data; + b = n->buf; + + /* whole buf */ + if (e->length == (apr_size_t)ngx_buf_size(b)) { + b->last_buf = 0; + return b; + } + + buf = ngx_palloc(pool, sizeof(ngx_buf_t)); + if (buf == NULL) { + return NULL; + } + ngx_memcpy(buf, b, sizeof(ngx_buf_t)); + + if (ngx_buf_in_memory(buf)) { + buf->start = buf->pos = buf->pos + e->start; + buf->end = buf->last = buf->pos + e->length; + } else { + buf->pos = NULL; + buf->file_pos += e->start; + buf->file_last = buf->file_pos + e->length; + } + + buf->last_buf = 0; + return buf; + } + + if (apr_bucket_read(e, (const char **)&data, + &len, APR_BLOCK_READ) != APR_SUCCESS) { + return NULL; + } + + buf = ngx_calloc_buf(pool); + if (buf == NULL) { + return NULL; + } + + if (e->type == &apr_bucket_type_pool) { + buf->start = data; + } else if (len != 0) { + buf->start = ngx_palloc(pool, len); + ngx_memcpy(buf->start, data, len); + } + + buf->pos = buf->start; + buf->end = buf->last = buf->start + len; + buf->temporary = 1; + return buf; +} + +ngx_int_t +move_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *pool) { + apr_bucket *e; + ngx_chain_t *cl; + + while (chain) { + e = ngx_buf_to_apr_bucket(chain->buf, bb->p, bb->bucket_alloc); + if (e == NULL) { + return NGX_ERROR; + } + + APR_BRIGADE_INSERT_TAIL(bb, e); + 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; + chain = chain->next; + ngx_free_chain(pool, cl); + } + return NGX_AGAIN; +} + +ngx_int_t +move_brigade_to_chain(apr_bucket_brigade *bb, ngx_chain_t **ll, ngx_pool_t *pool) { + apr_bucket *e; + ngx_buf_t *buf; + ngx_chain_t *cl; + + cl = NULL; + + if (APR_BRIGADE_EMPTY(bb)) { + *ll = NULL; + return NGX_OK; + } + + for (e = APR_BRIGADE_FIRST(bb); + e != APR_BRIGADE_SENTINEL(bb); + e = APR_BUCKET_NEXT(e)) { + + if (APR_BUCKET_IS_EOS(e)) { + if (cl == NULL) { + *ll = cl; + } else { + cl->buf->last_buf = 1; + } + apr_brigade_cleanup(bb); + return NGX_OK; + } + + if (APR_BUCKET_IS_METADATA(e)) { + continue; + } + + buf = apr_bucket_to_ngx_buf(e, pool); + if (buf == NULL) { + break; + } + + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + break; + } + + cl->buf = buf; + cl->next = NULL; + *ll = cl; + ll = &cl->next; + } + + apr_brigade_cleanup(bb); + /* no eos or error */ + return NGX_ERROR; +} + diff --git a/nginx/modsecurity/apr_bucket_nginx.h b/nginx/modsecurity/apr_bucket_nginx.h new file mode 100644 index 0000000000..4dd1ca0ff1 --- /dev/null +++ b/nginx/modsecurity/apr_bucket_nginx.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include "apr_buckets.h" + +apr_bucket * apr_bucket_nginx_create(ngx_buf_t *buf, + apr_pool_t *p, + apr_bucket_alloc_t *list); + +apr_bucket * apr_bucket_nginx_make(apr_bucket *e, ngx_buf_t *buf, + apr_pool_t *pool); + +#define ngx_buf_to_apr_bucket apr_bucket_nginx_create + +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 move_brigade_to_chain(apr_bucket_brigade *bb, ngx_chain_t **chain, ngx_pool_t *pool); + diff --git a/nginx/modsecurity/config b/nginx/modsecurity/config index ce05d8a8e5..ecd8aa90f7 100644 --- a/nginx/modsecurity/config +++ b/nginx/modsecurity/config @@ -1,6 +1,8 @@ ngx_addon_name=ngx_http_modsecurity -HTTP_MODULES="$HTTP_MODULES ngx_http_modsecurity" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_modsecurity.c" +# HTTP_MODULES="$HTTP_MODULES ngx_http_modsecurity" +HTTP_HEADERS_FILTER_MODULE="ngx_http_modsecurity $HTTP_HEADERS_FILTER_MODULE" +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_modsecurity.c $ngx_addon_dir/apr_bucket_nginx.c" NGX_ADDON_DEPS="$NGX_ADDON_DEPS" CORE_LIBS="$CORE_LIBS $ngx_addon_dir/../../standalone/.libs/standalone.a -lapr-1 -laprutil-1 -lxml2 -lm" -CORE_INCS="$CORE_INCS /usr/include/apache2 /usr/include/apr-1.0 $ngx_addon_dir/../../standalone $ngx_addon_dir/../../apache2 /usr/include/libxml2" +CORE_INCS="$CORE_INCS /usr/include/apache2 /usr/include/apr-1.0 $ngx_addon_dir/../../standalone $ngx_addon_dir/../../apache2 /usr/include/libxml2 /usr/include/apr-1 /usr/include/httpd $ngx_addon_dir" +have=REQUEST_EARLY . auto/have diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 8945508ca1..1dabf8a46c 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -20,7 +20,7 @@ #include #include #include - +#include #undef CR #undef LF #undef CRLF @@ -32,17 +32,18 @@ typedef struct { ngx_flag_t enable; directory_config *config; + + 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_chain_t *chain; - ngx_buf_t buf; - void **loc_conf; - unsigned request_body_in_single_buf:1; - unsigned request_body_in_file_only:1; + + apr_bucket_brigade *brigade; + unsigned complete; } ngx_http_modsecurity_ctx_t; @@ -50,7 +51,9 @@ 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_request_body_handler(ngx_http_request_t *r); +static void ngx_http_modsecurity_body_handler(ngx_http_request_t *r); +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); 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); @@ -58,13 +61,12 @@ static void ngx_http_modsecurity_exit_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); -apr_status_t modsecurity_read_body_cb(request_rec *r, char *buf, unsigned int length, - unsigned int *readcnt, int *is_eos); -apr_status_t modsecurity_write_body_cb(request_rec *rec, char *buf, unsigned int length); +static char *ngx_http_modsecurity_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_http_modsecurity_ctx_t * ngx_http_modsecurity_create_ctx(ngx_http_request_t *r); static int ngx_http_modsecurity_drop_action(request_rec *r); static void ngx_http_modsecurity_cleanup(void *data); +static char *ConvertNgxStringToUTF8(ngx_str_t str, apr_pool_t *pool); /* command handled by the module */ static ngx_command_t ngx_http_modsecurity_commands[] = { @@ -77,7 +79,7 @@ static ngx_command_t ngx_http_modsecurity_commands[] = { { ngx_string("ModSecurityEnabled"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF |NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, - ngx_conf_set_flag_slot, + ngx_http_modsecurity_enable, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_modsecurity_loc_conf_t, enable), NULL }, @@ -118,6 +120,40 @@ ngx_module_t ngx_http_modsecurity = { NGX_MODULE_V1_PADDING }; +static inline ngx_int_t +ngx_list_copy_to_apr_table(ngx_list_t *list, apr_table_t *table, apr_pool_t *pool) { + ngx_list_part_t *part; + ngx_table_elt_t *h; + ngx_uint_t i; + char *key, *value; + + part = &list->part; + h = part->elts; + + for (i = 0; ; i++) { + if (i >= part->nelts) { + if (part->next == NULL) + break; + + part = part->next; + h = part->elts; + i = 0; + } + + key = ConvertNgxStringToUTF8(h[i].key, pool); + if (key == NULL) { + return NGX_ERROR; + } + + value = ConvertNgxStringToUTF8(h[i].value, pool); + if (value == NULL) { + return NGX_ERROR; + } + + apr_table_setn(table, key, value); + } + return NGX_OK; +} /* create loc conf struct */ static void * @@ -125,10 +161,11 @@ ngx_http_modsecurity_create_loc_conf(ngx_conf_t *cf) { ngx_http_modsecurity_loc_conf_t *conf; - conf = (ngx_http_modsecurity_loc_conf_t *) ngx_pcalloc(cf->pool, sizeof(ngx_http_modsecurity_loc_conf_t)); + conf = (ngx_http_modsecurity_loc_conf_t *) ngx_palloc(cf->pool, sizeof(ngx_http_modsecurity_loc_conf_t)); if (conf == NULL) return NULL; + conf->config = NGX_CONF_UNSET_PTR; conf->enable = NGX_CONF_UNSET; return conf; @@ -142,16 +179,20 @@ ngx_http_modsecurity_merge_loc_conf(ngx_conf_t *cf, void *parent, ngx_http_modsecurity_loc_conf_t *prev = parent; ngx_http_modsecurity_loc_conf_t *conf = child; - if (conf->config == NULL) { - conf->config = prev->config; - } - ngx_conf_merge_value(conf->enable, prev->enable, 0); + ngx_conf_merge_ptr_value(conf->config, prev->config, NULL); + + if (conf->enable && conf->config == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "ModSecurity: enabled in %V:%ui while no config file is specified ", + conf->file, conf->line); + return NGX_CONF_ERROR; + } return NGX_CONF_OK; } -void +static void modsecLog(void *obj, int level, char *str) { if (obj != NULL) { @@ -169,13 +210,13 @@ modsecLog(void *obj, int level, char *str) */ extern apr_pool_t *pool; -void * +static void * modsec_pcre_malloc(size_t size) { return apr_palloc(pool, size); } -void +static void modsec_pcre_free(void *ptr) { } @@ -190,8 +231,6 @@ ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) modsecSetLogHook(cf->log, modsecLog); modsecSetDropAction(ngx_http_modsecurity_drop_action); - modsecSetReadBody(modsecurity_read_body_cb); - modsecSetWriteBody(modsecurity_write_body_cb); modsecInit(); modsecStartConfig(); @@ -199,6 +238,9 @@ ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) return NGX_OK; } +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; + static ngx_int_t ngx_http_modsecurity_init(ngx_conf_t *cf) { @@ -218,20 +260,11 @@ ngx_http_modsecurity_init(ngx_conf_t *cf) } *h = ngx_http_modsecurity_handler; -#ifdef PROCESS_RESPONSE - /* - ** This function sets up handlers for CONTENT_PHASE, - ** XXX: not implemented yet - */ - - /* Register for CONTENT phase ?? */ - h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); - if (h == NULL) { - return NGX_ERROR; - } - *h = ngx_http_modsecurity_content_handler; -#endif + 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; return NGX_OK; } @@ -251,10 +284,14 @@ ngx_http_modsecurity_exit_process(ngx_cycle_t *cycle) } -char * +static char * ConvertNgxStringToUTF8(ngx_str_t str, apr_pool_t *pool) { char *t = (char *) apr_palloc(pool, str.len + 1); + + if (!t) { + return NULL; + } ngx_memcpy(t, str.data, str.len); t[str.len] = 0; @@ -262,114 +299,7 @@ ConvertNgxStringToUTF8(ngx_str_t str, apr_pool_t *pool) return t; } -/* -** request body callback, passing body to mod security -*/ -apr_status_t -modsecurity_read_body_cb(request_rec *r, char *outpos, unsigned int length, - unsigned int *outlen, int *is_eos) -{ - size_t len, rest; - ssize_t size; - ngx_http_modsecurity_ctx_t *ctx; - - ctx = (ngx_http_modsecurity_ctx_t *) apr_table_get(r->notes, NOTE_NGINX_REQUEST_CTX); - if (ctx == NULL) { - return APR_EINVAL; - } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "modSecurity: read_body_cb"); - - 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; - } - - ngx_memcpy(buf, ctx->chain->buf, sizeof(ngx_buf_t)); - ctx->chain = ctx->chain->next; - } - - 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 { - return -1; - } - } - - *outlen = length - rest; - return APR_SUCCESS; -} - -apr_status_t -modsecurity_write_body_cb(request_rec *rec, char *buf, unsigned int length) -{ - ngx_buf_t *b; - ngx_http_modsecurity_ctx_t *ctx; - ngx_http_request_t *r; - ngx_str_t *str; - - ctx = (ngx_http_modsecurity_ctx_t *) apr_table_get(rec->notes, NOTE_NGINX_REQUEST_CTX); - if (ctx == NULL) { - return APR_EINVAL; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->r->connection->log, 0, "modSecurity: write_body_cb"); - - r = ctx->r; - - /* set request body */ - b = r->header_in; - - if (b->end < b->pos + length) { - b->start = ngx_palloc(ctx->r->pool, length); - if (b->start == NULL) { - return APR_EINVAL; - } - b->end = b->start + length; - b->pos = b->start; - } - - b->last = ngx_cpymem(b->pos, buf, length); - - /* set content_length_n */ - r->headers_in.content_length_n = length; - - /* set headers_in.content_length */ - str = &r->headers_in.content_length->value; - str->data = ngx_palloc(r->pool, NGX_OFF_T_LEN); - if (str->data == NULL) { - return NGX_ERROR; - } - - str->len = ngx_snprintf(str->data, NGX_OFF_T_LEN, "%O", length) - str->data; - - return APR_SUCCESS; -} - -apr_sockaddr_t *CopySockAddr(apr_pool_t *pool, struct sockaddr *pAddr) { +static apr_sockaddr_t *CopySockAddr(apr_pool_t *pool, struct sockaddr *pAddr) { apr_sockaddr_t *addr = (apr_sockaddr_t *)apr_palloc(pool, sizeof(apr_sockaddr_t)); int adrlen = 16, iplen = 4; @@ -411,21 +341,15 @@ static ngx_int_t ngx_http_modsecurity_handler(ngx_http_request_t *r) { ngx_http_modsecurity_loc_conf_t *cf; - ngx_http_core_loc_conf_t *clcf, *lcf; ngx_http_modsecurity_ctx_t *ctx; ngx_int_t rc; - void **loc_conf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: handler"); - /* Process only main request */ - if (r != r->main || r->internal) { - return NGX_DECLINED; - } - cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); - if (!cf->enable) { + /* Process only main request */ + if (r != r->main || r->internal || !cf->enable) { return NGX_DECLINED; } @@ -435,98 +359,252 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) } ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); - 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"); + /* processing request headers */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: process request headers"); + + rc = modsecProcessRequestHeaders(ctx->req); - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (rc == DECLINED) { + 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 (clcf == NULL) { + 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 */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: process request body"); + rc = modsecProcessRequestBody(ctx->req); + } + + if (rc != DECLINED) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: status: %d, need action", rc); + + /* Nginx and Apache share same response code */ + if (rc < NGX_HTTP_SPECIAL_RESPONSE || rc >= 600) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } + return rc; + } - 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 < (size_t)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; - } + return NGX_DECLINED; +} - lcf = ngx_pcalloc(r->pool, sizeof(ngx_http_core_loc_conf_t)); - if (lcf == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + +static void +ngx_http_modsecurity_body_handler(ngx_http_request_t *r) +{ + ngx_http_modsecurity_ctx_t *ctx; + ngx_int_t rc; + apr_off_t content_length; + ngx_str_t *str; + ngx_buf_t *buf; + + if (r->request_body == NULL || r->request_body->bufs == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: request body empty"); + r->phase_handler++; + ngx_http_core_run_phases(r); + ngx_http_finalize_request(r, NGX_DONE); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: process request body"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + + rc = move_chain_to_brigade(r->request_body->bufs, ctx->brigade, r->pool); + if (rc == NGX_ERROR) { + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + r->request_body = NULL; + + modsecSetBodyBrigade(ctx->req, ctx->brigade); - 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; + rc = modsecProcessRequestBody(ctx->req); - 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; + if (rc != DECLINED) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: status: %d, need action", rc); + + /* Nginx and Apache share same response code */ + if (rc < NGX_HTTP_SPECIAL_RESPONSE || rc >= 600) { + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } - - 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; + return ngx_http_finalize_request(r, rc); + } - rc = ngx_http_read_client_request_body(r, ngx_http_modsecurity_request_body_handler); - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return rc; + apr_brigade_length(ctx->brigade, 0, &content_length); + buf = ngx_create_temp_buf(ctx->r->pool, (size_t) content_length); + if (buf == NULL){ + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + if (apr_brigade_flatten(ctx->brigade, (char *)buf->pos, (apr_size_t *)&content_length) != APR_SUCCESS) { + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + apr_brigade_cleanup(ctx->brigade); + + buf->last += content_length; + r->header_in = buf; + + if (r->headers_in.content_length) { + str = &r->headers_in.content_length->value; + str->data = ngx_palloc(r->pool, NGX_OFF_T_LEN); + if (str->data == NULL) { + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } + str->len = ngx_snprintf(str->data, NGX_OFF_T_LEN, "%O", content_length) - str->data; + } + + r->headers_in.content_length_n = content_length; + + r->phase_handler++; + ngx_http_core_run_phases(r); + ngx_http_finalize_request(r, NGX_DONE); +} + + +static ngx_int_t +ngx_http_modsecurity_header_filter(ngx_http_request_t *r) { + ngx_http_modsecurity_loc_conf_t *cf; + ngx_http_modsecurity_ctx_t *ctx; + const char *lang; + + cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); - return NGX_DONE; + if (r != r->main || r->internal || !cf->enable || ctx->complete) { + return ngx_http_next_header_filter(r); + } + + if (r->method == NGX_HTTP_HEAD || r->header_only + || !modsecIsResponseBodyAccessEnabled(ctx->req) ) { + /* TODO: RESPONSE HEADERS PHASE + */ + return ngx_http_next_header_filter(r); + } + + /* copy headers_out */ + if (ngx_list_copy_to_apr_table(&r->headers_out.headers, + ctx->req->headers_out, + ctx->req->pool) != NGX_OK) { + + return NGX_ERROR; + } + + ctx->req->content_type = ConvertNgxStringToUTF8(r->headers_out.content_type, ctx->req->pool); + ctx->req->content_encoding = apr_table_get(ctx->req->headers_out, "Content-Encoding"); + lang = apr_table_get(ctx->req->headers_out, "Content-Languages"); + + if(lang != NULL) + { + ctx->req->content_languages = apr_array_make(ctx->req->pool, 1, sizeof(const char *)); + *(const char **)apr_array_push(ctx->req->content_languages) = lang; + } - } else { - /* processing all the other methods */ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: method is not POST"); + return NGX_OK; +} + +static ngx_int_t +ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_http_modsecurity_loc_conf_t *cf; + ngx_http_modsecurity_ctx_t *ctx; + ngx_int_t rc; + apr_off_t content_length; + + cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + + if (r != r->main || r->internal || !cf->enable || ctx->complete) { + return ngx_http_next_body_filter(r, in); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: body filter"); + + if (in == NULL) { + return NGX_AGAIN; + } + + rc = move_chain_to_brigade(in, ctx->brigade, r->pool); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } else if (rc == NGX_AGAIN) { + return NGX_AGAIN; + } + + /* last buf has been saved */ + + ctx->complete = 1; + modsecSetResponseBrigade(ctx->req, ctx->brigade); + + rc = modsecProcessResponse(ctx->req); + + if (rc == DECLINED || rc == APR_SUCCESS) { + + in = NULL; + + apr_brigade_length(ctx->brigade, 0, &content_length); - rc = modsecProcessRequest(ctx->req); + rc = move_brigade_to_chain(ctx->brigade, &in, ctx->r->pool); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } - if (rc != DECLINED) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: status: %d, need action", rc); + if (r->headers_out.content_length_n != -1) { + + r->headers_out.content_length_n = content_length; + r->headers_out.content_length = NULL; /* header filter will set this */ + } + + rc = ngx_http_next_header_filter(r); - ngx_http_clear_accept_ranges(r); - ngx_http_clear_last_modified(r); - ngx_http_clear_content_length(r); + if (rc == NGX_ERROR || rc > NGX_OK) { + return rc; + } - /* Nginx and Apache share same response code */ - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + rc = ngx_http_next_body_filter(r, in); + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_OK; + } + + if (rc < NGX_HTTP_SPECIAL_RESPONSE || rc >= 600) { + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } - return NGX_DECLINED; + return rc; /* ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, rc); */ } + static ngx_http_modsecurity_ctx_t * ngx_http_modsecurity_create_ctx(ngx_http_request_t *r) { ngx_http_modsecurity_loc_conf_t *cf; ngx_pool_cleanup_t *cln; ngx_http_modsecurity_ctx_t *ctx; - ngx_list_part_t *part; - ngx_table_elt_t *h; - ngx_uint_t i; + const char *lang; + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_modsecurity_ctx_t)); + if (ctx == NULL) { + 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"); return NULL; } cln->handler = ngx_http_modsecurity_cleanup; - ngx_memzero(cln->data, sizeof(ngx_http_modsecurity_ctx_t)); + cln->data = ctx; - ctx = cln->data; ctx->r = r; if (r->connection->requests == 0 || ctx->connection == NULL) { @@ -565,27 +643,31 @@ ngx_http_modsecurity_create_ctx(ngx_http_request_t *r) ctx->req->parsed_uri.user = NULL; ctx->req->parsed_uri.fragment = ConvertNgxStringToUTF8(r->exten, ctx->req->pool); - part = &r->headers_in.headers.part; - h = part->elts; + if (ngx_list_copy_to_apr_table(&r->headers_in.headers, + ctx->req->headers_in, + ctx->req->pool) != NGX_OK) { + return NULL; + } - for (i = 0; ; i++) { - if (i >= part->nelts) { - if (part->next == NULL) - break; + /* XXX: if mod_uniqid enabled - use it's value */ + apr_table_setn(ctx->req->subprocess_env, "UNIQUE_ID", "12345"); - part = part->next; - h = part->elts; - i = 0; - } + ctx->req->content_encoding = apr_table_get(ctx->req->headers_in, "Content-Encoding"); + ctx->req->content_type = apr_table_get(ctx->req->headers_in, "Content-Type"); + + lang = apr_table_get(ctx->req->headers_in, "Content-Languages"); + if(lang != NULL) + { + ctx->req->content_languages = apr_array_make(ctx->req->pool, 1, sizeof(const char *)); - apr_table_setn(ctx->req->headers_in, ConvertNgxStringToUTF8(h[i].key, ctx->req->pool), - ConvertNgxStringToUTF8(h[i].value, ctx->req->pool)); + *(const char **)apr_array_push(ctx->req->content_languages) = lang; } - /* XXX: if mod_uniqid enabled - use it's value */ - 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); + ctx->brigade = apr_brigade_create(ctx->req->pool, ctx->req->connection->bucket_alloc); + + if (ctx->brigade == NULL) { + return NULL; + } return ctx; } @@ -599,52 +681,6 @@ ngx_http_modsecurity_cleanup(void *data) } } - -static void -ngx_http_modsecurity_request_body_handler(ngx_http_request_t *r) -{ - ngx_http_modsecurity_ctx_t *ctx; - ngx_int_t rc; - - 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; - - rc = modsecProcessRequest(ctx->req); - - if (rc != DECLINED) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: status: %d, need action", rc); - - ngx_http_clear_accept_ranges(r); - ngx_http_clear_last_modified(r); - ngx_http_clear_content_length(r); - - /* Nginx and Apache share same response code */ - if (rc < NGX_HTTP_SPECIAL_RESPONSE) { - rc = NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - ngx_http_finalize_request(r, rc); - } - - r->phase_handler++; - ngx_http_core_run_phases(r); - ngx_http_finalize_request(r, NGX_DONE); -} - - static char * ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -652,7 +688,7 @@ ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_str_t *value; const char *msg; - if (mscf->config != NULL) { + if (mscf->config != NGX_CONF_UNSET_PTR) { return "is duplicate"; } @@ -664,14 +700,35 @@ ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) mscf->config = modsecGetDefaultConfig(); + if (mscf->config == NULL) { + return NGX_CONF_ERROR; + } + msg = modsecProcessConfig(mscf->config, (const char *)value[1].data); if (msg != NULL) { - ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "modSecurity: modsecProcessConfig() %s", msg); + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "modSecurity: modsecProcessConfig() %s", msg); return NGX_CONF_ERROR; } return NGX_CONF_OK; } +static char * +ngx_http_modsecurity_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_modsecurity_loc_conf_t *mscf = conf; + char *rc; + + rc = ngx_conf_set_flag_slot(cf, cmd, conf); + if (rc != NGX_CONF_OK) { + return rc; + } + if (mscf->enable) { + mscf->file = &cf->conf_file->file.name; + mscf->line = cf->conf_file->line; + } + return NGX_CONF_OK; +} + static int ngx_http_modsecurity_drop_action(request_rec *r) @@ -685,3 +742,4 @@ ngx_http_modsecurity_drop_action(request_rec *r) ctx->r->connection->error = 1; return 0; } + diff --git a/standalone/Makefile.in b/standalone/Makefile.in index aa6e8acf7c..0d23ac0823 100644 --- a/standalone/Makefile.in +++ b/standalone/Makefile.in @@ -1,9 +1,9 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.11.3 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -73,6 +73,12 @@ am__nobase_list = $(am__nobase_strip_setup); \ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } am__installdirs = "$(DESTDIR)$(pkglibdir)" LTLIBRARIES = $(pkglib_LTLIBRARIES) standalone_la_DEPENDENCIES = @@ -166,6 +172,7 @@ CURL_VERSION = @CURL_VERSION@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -200,6 +207,7 @@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LDADD = @LUA_LDADD@ LUA_LDFLAGS = @LUA_LDFLAGS@ MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MODSEC_APXS_EXTRA_CFLAGS = @MODSEC_APXS_EXTRA_CFLAGS@ MODSEC_EXTRA_CFLAGS = @MODSEC_EXTRA_CFLAGS@ @@ -230,6 +238,7 @@ PCRE_CONFIG = @PCRE_CONFIG@ PCRE_CPPFLAGS = @PCRE_CPPFLAGS@ PCRE_LDADD = @PCRE_LDADD@ PCRE_LDFLAGS = @PCRE_LDFLAGS@ +PCRE_LD_PATH = @PCRE_LD_PATH@ PCRE_VERSION = @PCRE_VERSION@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ @@ -246,6 +255,7 @@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ @@ -278,7 +288,6 @@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ -lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ @@ -415,7 +424,7 @@ clean-pkglibLTLIBRARIES: echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done -standalone.la: $(standalone_la_OBJECTS) $(standalone_la_DEPENDENCIES) +standalone.la: $(standalone_la_OBJECTS) $(standalone_la_DEPENDENCIES) $(EXTRA_standalone_la_DEPENDENCIES) $(standalone_la_LINK) -rpath $(pkglibdir) $(standalone_la_OBJECTS) $(standalone_la_LIBADD) $(LIBS) mostlyclean-compile: @@ -815,10 +824,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: diff --git a/standalone/api.c b/standalone/api.c index 966d824bd4..b5fea42eb3 100644 --- a/standalone/api.c +++ b/standalone/api.c @@ -38,6 +38,7 @@ #include "ap_config.h" #include "http_config.h" +#include "api.h" extern void *modsecLogObj; extern void (*modsecLogHook)(void *obj, int level, char *str); @@ -143,17 +144,17 @@ server_rec *modsecInit() { server->server_scheme = ""; server->timeout = 60 * 1000000;// 60 seconds server->wild_names = NULL; - server->is_virtual = 0; + server->is_virtual = 0; ap_server_config_defines = apr_array_make(pool, 1, sizeof(char *)); // here we should add scoreboard handling for multiple processes and threads - // + // ap_scoreboard_image = (scoreboard *)apr_palloc(pool, sizeof(scoreboard)); - memset(ap_scoreboard_image, 0, sizeof(scoreboard)); + memset(ap_scoreboard_image, 0, sizeof(scoreboard)); - // ---------- + // ---------- security2_module.module_index = 0; @@ -165,51 +166,91 @@ server_rec *modsecInit() { return server; } -apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *b, +apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { char *tmp = NULL; apr_bucket *e = NULL; unsigned int readcnt = 0; int is_eos = 0; + apr_bucket_brigade *bb_in; + apr_bucket *after; + apr_status_t rv; - if(modsecReadBody == NULL) - return AP_NOBODY_READ; + bb_in = modsecGetBodyBrigade(f->r); - tmp = (char *)apr_palloc(f->r->pool, readbytes); - modsecReadBody(f->r, tmp, readbytes, &readcnt, &is_eos); + /* use request brigade */ + if (bb_in != NULL) { + if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb_in))) { + e = apr_bucket_eos_create(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb_in, e); + } - e = apr_bucket_pool_create(tmp, readcnt, f->r->pool, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(b, e); + rv = apr_brigade_partition(bb_in, readbytes, &after); + if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { + return rv; + } + + for (e = APR_BRIGADE_FIRST(bb_in); e != after; e = APR_BRIGADE_FIRST(bb_in)) { + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(bb_out, e); + } - if(is_eos) { - e = apr_bucket_eos_create(f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(b, e); + return APR_SUCCESS; } - return APR_SUCCESS; + /* call the callback */ + if(modsecReadBody != NULL) { + + tmp = (char *)apr_palloc(f->r->pool, readbytes); + modsecReadBody(f->r, tmp, readbytes, &readcnt, &is_eos); + + e = apr_bucket_pool_create(tmp, readcnt, f->r->pool, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb_out, e); + + if(is_eos) { + e = apr_bucket_eos_create(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb_out, e); + } + return APR_SUCCESS; + } + + return AP_NOBODY_READ; } apr_status_t ap_http_out_filter(ap_filter_t *f, apr_bucket_brigade *b) { modsec_rec *msr = (modsec_rec *)f->ctx; apr_status_t rc; + apr_bucket_brigade *bb_out; + + bb_out = modsecGetResponseBrigade(f->r); + + + if (bb_out) { + APR_BRIGADE_CONCAT(bb_out, b); + return APR_SUCCESS; + } // is there a way to tell whether the response body was modified or not? // if((msr->txcfg->content_injection_enabled || msr->content_prepend_len != 0 || msr->content_append_len != 0) - && modsecWriteResponse != NULL && msr->txcfg->resbody_access) { - char *data = NULL; - apr_size_t length; + && msr->txcfg->resbody_access) { - rc = apr_brigade_pflatten(msr->of_brigade, &data, &length, msr->mp); + if (modsecWriteResponse != NULL) { + char *data = NULL; + apr_size_t length; - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Output filter: Failed to flatten brigade (%d): %s", rc, - get_apr_error(msr->mp, rc)); - return -1; - } + rc = apr_brigade_pflatten(msr->of_brigade, &data, &length, msr->mp); + + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "Output filter: Failed to flatten brigade (%d): %s", rc, + get_apr_error(msr->mp, rc)); + return -1; + } - modsecWriteResponse(msr->r, data, msr->stream_output_length); + /* TODO: return ?*/ + modsecWriteResponse(msr->r, data, msr->stream_output_length); + } } return APR_SUCCESS; @@ -385,13 +426,17 @@ static modsec_rec *retrieve_msr(request_rec *r) { return NULL; } -int modsecProcessRequest(request_rec *r) { +int modsecProcessRequestHeaders(request_rec *r) { + return hookfn_post_read_request(r); +} + +int modsecProcessRequestBody(request_rec *r) { int status = DECLINED; modsec_rec *msr = NULL; ap_filter_t *f = ap_add_input_filter("HTTP_IN", NULL, r, r->connection); + apr_bucket_brigade* bb_out; - status = hookfn_post_read_request(r); status = hookfn_fixups(r); ap_remove_input_filter(f); @@ -404,6 +449,16 @@ int modsecProcessRequest(request_rec *r) { if (msr == NULL) return status; + bb_out = modsecGetBodyBrigade(r); + if (bb_out) { + (void) apr_brigade_cleanup(bb_out); + status = ap_get_brigade(r->input_filters, bb_out, AP_MODE_READBYTES, APR_BLOCK_READ, -1); + if (status == APR_SUCCESS) { + return DECLINED; + } + return status; + } + if(msr->stream_input_data != NULL && modsecWriteBody != NULL) { // target is responsible for copying the data into correctly managed buffer @@ -425,14 +480,15 @@ int modsecProcessRequest(request_rec *r) { return status; } + int modsecIsResponseBodyAccessEnabled(request_rec *r) { - modsec_rec *msr = retrieve_msr(r); + modsec_rec *msr = retrieve_msr(r); - if(msr == NULL || msr->txcfg == NULL) - return 0; + if(msr == NULL || msr->txcfg == NULL) + return 0; - return msr->txcfg->resbody_access; + return msr->txcfg->resbody_access; } int modsecProcessResponse(request_rec *r) { @@ -446,7 +502,7 @@ int modsecProcessResponse(request_rec *r) { unsigned int readcnt = 0; int is_eos = 0; ap_filter_t *f = NULL; - apr_bucket_brigade *bb = NULL; + apr_bucket_brigade *bb_in, *bb = NULL; if (msr == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, @@ -463,33 +519,39 @@ int modsecProcessResponse(request_rec *r) { } msr->r = r; + + bb_in = modsecGetResponseBrigade(r); - if(modsecReadResponse == NULL) - return AP_NOBODY_WROTE; - - f = ap_add_output_filter("HTTP_OUT", msr, r, r->connection); - - while(!is_eos) { - modsecReadResponse(r, buf, 8192, &readcnt, &is_eos); - - if(readcnt > 0) { - tmp = (char *)apr_palloc(r->pool, readcnt); - memcpy(tmp, buf, readcnt); - - e = apr_bucket_pool_create(tmp, readcnt, r->pool, r->connection->bucket_alloc); + if (bb_in != NULL) { + APR_BRIGADE_CONCAT(bb, bb_in); + if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { + e = apr_bucket_eos_create(bb->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); } - - if(is_eos) { - e = apr_bucket_eos_create(r->connection->bucket_alloc); - - APR_BRIGADE_INSERT_TAIL(bb, e); + } else if (modsecReadResponse != NULL) { + while(!is_eos) { + modsecReadResponse(r, buf, 8192, &readcnt, &is_eos); + + if(readcnt > 0) { + tmp = (char *)apr_palloc(r->pool, readcnt); + memcpy(tmp, buf, readcnt); + e = apr_bucket_pool_create(tmp, readcnt, r->pool, r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } + + if(is_eos) { + e = apr_bucket_eos_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } } + } else { + return AP_NOBODY_WROTE; } - + + f = ap_add_output_filter("HTTP_OUT", msr, r, r->connection); status = ap_pass_brigade(r->output_filters, bb); - ap_remove_output_filter(f); + return status; } return status; diff --git a/standalone/api.h b/standalone/api.h index 825f722cc4..8516ce2c67 100644 --- a/standalone/api.h +++ b/standalone/api.h @@ -60,12 +60,49 @@ conn_rec *modsecNewConnection(); void modsecProcessConnection(conn_rec *c); request_rec *modsecNewRequest(conn_rec *connection, directory_config *config); -int modsecProcessRequest(request_rec *r); + +int modsecProcessRequestBody(request_rec *r); +int modsecProcessRequestHeaders(request_rec *r); + +static inline int modsecProcessRequest(request_rec *r) { + int status; + status = modsecProcessRequestHeaders(r); + if (status != DECLINED) { + return status; + } + + return modsecProcessRequestBody(r); +} + + int modsecProcessResponse(request_rec *r); int modsecFinishRequest(request_rec *r); void modsecSetLogHook(void *obj, void (*hook)(void *obj, int level, char *str)); +#define NOTE_MSR_BRIGADE_REQUEST "modsecurity-brigade-request" +#define NOTE_MSR_BRIGADE_RESPONSE "modsecurity-brigade-response" + +static inline void +modsecSetBodyBrigade(request_rec *r, apr_bucket_brigade *b) { + apr_table_setn(r->notes, NOTE_MSR_BRIGADE_REQUEST, (char *)b); +}; + +static inline apr_bucket_brigade * +modsecGetBodyBrigade(request_rec *r) { + return (apr_bucket_brigade *)apr_table_get(r->notes, NOTE_MSR_BRIGADE_REQUEST); +}; + +static inline void +modsecSetResponseBrigade(request_rec *r, apr_bucket_brigade *b) { + apr_table_setn(r->notes, NOTE_MSR_BRIGADE_RESPONSE, (char *)b); +}; + +static inline apr_bucket_brigade * +modsecGetResponseBrigade(request_rec *r) { + return (apr_bucket_brigade *)apr_table_get(r->notes, NOTE_MSR_BRIGADE_RESPONSE); +}; + void modsecSetReadBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)); void modsecSetReadResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)); void modsecSetWriteBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length));