From 82894ec15e6e1facd56a219ac86703b50cb12047 Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Mon, 14 Jan 2013 18:07:01 +0800 Subject: [PATCH 01/11] Added: convert ngx_chain to apr_brigade --- nginx/modsecurity/apr_bucket_nginx.c | 209 +++++++++++++ nginx/modsecurity/apr_bucket_nginx.h | 24 ++ nginx/modsecurity/config | 7 +- nginx/modsecurity/ngx_http_modsecurity.c | 366 ++++++++++++++++++----- standalone/Makefile.in | 32 +- standalone/api.c | 55 +++- standalone/api.h | 23 ++ 7 files changed, 620 insertions(+), 96 deletions(-) create mode 100644 nginx/modsecurity/apr_bucket_nginx.c create mode 100644 nginx/modsecurity/apr_bucket_nginx.h diff --git a/nginx/modsecurity/apr_bucket_nginx.c b/nginx/modsecurity/apr_bucket_nginx.c new file mode 100644 index 0000000000..f4763c69da --- /dev/null +++ b/nginx/modsecurity/apr_bucket_nginx.c @@ -0,0 +1,209 @@ + +#include +#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(buf)) { + 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; + apr_bucket_nginx *n; + ngx_uint_t len; + u_char *data; + + if (e->type == &apr_bucket_type_nginx) { + n = e->data; + + buf = ngx_alloc_buf(pool); + if (buf == NULL) { + return NULL; + } + + ngx_memcpy(buf, n->buf, 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; + } + return buf; + } + + /* convert apr_bucket to ngx_buf_t */ + 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) { + /* TODO: may be we can deal more bucket type here */ + /* shadow copy */ + 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; +} + +apr_bucket_brigade * +ngx_chain_to_apr_brigade(ngx_chain_t *chain, apr_pool_t *p, apr_bucket_alloc_t *list) { + apr_bucket_brigade *b; + apr_bucket *e; + + b = apr_brigade_create(p, list); + + while (chain) { + e = ngx_buf_to_apr_bucket(chain->buf, p, list); + APR_BRIGADE_INSERT_TAIL(b, e); + chain = chain->next; + } + e = apr_bucket_eos_create(list); + APR_BRIGADE_INSERT_TAIL(b, e); + + return b; +} + +/* apr_brigade_destory could be called after converted */ +ngx_chain_t * +apr_brigade_to_ngx_chain(apr_bucket_brigade *b, ngx_pool_t *pool) { + apr_bucket *e; + ngx_buf_t *buf; + ngx_chain_t *cl, *tl, **ll; + + cl = NULL; + ll = &cl; + + for(e = APR_BRIGADE_FIRST(b); + e != APR_BRIGADE_SENTINEL(b); + e = APR_BUCKET_NEXT(e)) { + buf = apr_bucket_to_ngx_buf(e, pool); + if (buf == NULL) { + return NULL; + } + tl = ngx_alloc_chain_link(pool); + if (tl == NULL) { + return NULL; + } + + tl->buf = buf; + tl->next = NULL; + *ll = tl; + ll = &tl->next; + } + + if (cl == NULL) { + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = ngx_calloc_buf(pool); + if (cl->buf == NULL) { + return NULL; + } + + cl->next = NULL; + } + + return cl; +} + diff --git a/nginx/modsecurity/apr_bucket_nginx.h b/nginx/modsecurity/apr_bucket_nginx.h new file mode 100644 index 0000000000..2a9f4c6adb --- /dev/null +++ b/nginx/modsecurity/apr_bucket_nginx.h @@ -0,0 +1,24 @@ + +#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); + + +apr_bucket_brigade * +ngx_chain_to_apr_brigade(ngx_chain_t *chain, apr_pool_t *p, apr_bucket_alloc_t *list); + + +/* apr_brigade_destory could be called after converted */ +ngx_chain_t * +apr_brigade_to_ngx_chain(apr_bucket_brigade *b, ngx_pool_t *pool); + diff --git a/nginx/modsecurity/config b/nginx/modsecurity/config index ce05d8a8e5..c2e6896671 100644 --- a/nginx/modsecurity/config +++ b/nginx/modsecurity/config @@ -1,6 +1,7 @@ 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_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_modsecurity" +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" diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 8945508ca1..56ef06cdc6 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 @@ -38,11 +38,6 @@ 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; } ngx_http_modsecurity_ctx_t; @@ -58,6 +53,7 @@ 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); @@ -65,6 +61,7 @@ apr_status_t modsecurity_write_body_cb(request_rec *rec, char *buf, unsigned int 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); +char *ConvertNgxStringToUTF8(ngx_str_t str, apr_pool_t *pool); /* command handled by the module */ static ngx_command_t ngx_http_modsecurity_commands[] = { @@ -118,6 +115,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 * @@ -190,8 +221,11 @@ ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) modsecSetLogHook(cf->log, modsecLog); modsecSetDropAction(ngx_http_modsecurity_drop_action); +#if 0 modsecSetReadBody(modsecurity_read_body_cb); modsecSetWriteBody(modsecurity_write_body_cb); +#endif + /* ap_hook_insert_filter(ap_nginx_insert_filter, NULL, NULL, APR_HOOK_FIRST); */ modsecInit(); modsecStartConfig(); @@ -255,6 +289,10 @@ 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,6 +300,7 @@ ConvertNgxStringToUTF8(ngx_str_t str, apr_pool_t *pool) return t; } +#if 0 /* ** request body callback, passing body to mod security */ @@ -322,7 +361,6 @@ modsecurity_read_body_cb(request_rec *r, char *outpos, unsigned int length, *outlen = length - rest; return APR_SUCCESS; } - apr_status_t modsecurity_write_body_cb(request_rec *rec, char *buf, unsigned int length) { @@ -368,6 +406,7 @@ modsecurity_write_body_cb(request_rec *rec, char *buf, unsigned int length) return APR_SUCCESS; } +#endif 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)); @@ -411,10 +450,9 @@ 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_core_loc_conf_t *clcf; 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"); @@ -445,38 +483,6 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) 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 < (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; - } - - 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; @@ -507,6 +513,98 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) return NGX_DECLINED; } +#if 0 +static ngx_int_t +ngx_http_modsecurity_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + /* headers */ + 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_http_next_body_filter(r, in); + } + + cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); + + if (!cf->enable) { + return ngx_http_next_body_filter(r, in); + } + + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + if (ctx == NULL) { + ctx = ngx_http_modsecurity_create_ctx(r); + if (ctx == NULL) { + r->filter_finalize = 1; + return NGX_ERROR; + } + ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); + } + + if (ctx->phase < RESPONSE) { + ctx->chain_in = NULL; + if (ngx_list_copy_to_apr_table(&r->headers_out.headers, + ctx->req->headers_out, + ctx->req->pool) != NGX_OK) { + r->filter_finalize = 1; + return NGX_ERROR; + } + ctx->phase = RESPONSE; + } + + /* buffer chain */ + if (in != NULL) { + ngx_chain_t *cl, **ll; + ll = &ctx->chain_in; + + for (cl = ctx->chain_in; cl; cl = cl->next) { + ll = &cl->next; + } + + while (in) { + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + r->filter_finalize = 1; + return NGX_ERROR; + } + cl->buf = in->buf; + in = in->next; + cl->next = NULL; + *ll = cl; + ll = &cl->next; + } + + return NGX_AGAIN; + } + + /* ngx chain to apr brigade */ + + + /* process headers and buffered body */ + rc = modsecProcessResponse(ctx->req); + + if (rc == DECLINED) { + /* apr brigade to ngx chain */ + + + return ngx_http_next_body_filter(r, in); + } + + if (rc > NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + r->filter_finalize = 1; + return NGX_ERROR; +} +#endif static ngx_http_modsecurity_ctx_t * ngx_http_modsecurity_create_ctx(ngx_http_request_t *r) @@ -514,9 +612,6 @@ 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; cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_modsecurity_ctx_t)); if (cln == NULL) { @@ -565,21 +660,10 @@ 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; - - for (i = 0; ; i++) { - if (i >= part->nelts) { - if (part->next == NULL) - break; - - part = part->next; - h = part->elts; - i = 0; - } - - apr_table_setn(ctx->req->headers_in, ConvertNgxStringToUTF8(h[i].key, ctx->req->pool), - ConvertNgxStringToUTF8(h[i].value, ctx->req->pool)); + if (ngx_list_copy_to_apr_table(&r->headers_in.headers, + ctx->req->headers_in, + ctx->req->pool) != NGX_OK) { + return NULL; } /* XXX: if mod_uniqid enabled - use it's value */ @@ -604,24 +688,29 @@ static void ngx_http_modsecurity_request_body_handler(ngx_http_request_t *r) { ngx_http_modsecurity_ctx_t *ctx; + apr_bucket_brigade *bb; ngx_int_t rc; + apr_off_t len; + ngx_str_t *str; + + rc = NGX_DONE; 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->bufs == NULL ) { + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } - - 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; + + bb = ngx_chain_to_apr_brigade(r->request_body->bufs, + ctx->req->pool, + ctx->req->connection->bucket_alloc); r->request_body = NULL; - r->loc_conf = ctx->loc_conf; + if (bb == NULL) { + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + modsecSetBodyBrigade(ctx->req, bb); rc = modsecProcessRequest(ctx->req); if (rc != DECLINED) { @@ -632,16 +721,44 @@ ngx_http_modsecurity_request_body_handler(ngx_http_request_t *r) ngx_http_clear_content_length(r); /* Nginx and Apache share same response code */ - if (rc < NGX_HTTP_SPECIAL_RESPONSE) { + if (rc < NGX_HTTP_SPECIAL_RESPONSE || rc >= 600) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } - - ngx_http_finalize_request(r, rc); + return ngx_http_finalize_request(r, rc); + } + bb = modsecGetBodyBrigade(ctx->req); + + if (apr_brigade_length(bb, 1, &len) != APR_SUCCESS) { + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } + ngx_buf_t *buf = ngx_create_temp_buf(ctx->r->pool, (size_t) len); + if (buf == NULL){ + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + if (apr_brigade_flatten(bb, (char *)buf->pos, (apr_size_t *)&len) != APR_SUCCESS) { + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + apr_brigade_cleanup(bb); + + buf->last += len; + r->header_in = buf; + r->headers_in.content_length_n = len; + + /* 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_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + str->len = ngx_snprintf(str->data, NGX_OFF_T_LEN, "%O", len) - str->data; + r->phase_handler++; ngx_http_core_run_phases(r); - ngx_http_finalize_request(r, NGX_DONE); + ngx_http_finalize_request(r, NGX_DONE); } @@ -685,3 +802,106 @@ ngx_http_modsecurity_drop_action(request_rec *r) ctx->r->connection->error = 1; return 0; } + +#if 0 +static apr_status_t +ap_nginx_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, + ap_input_mode_t mode, apr_read_type_e block, + apr_off_t readbytes) { + + ngx_http_modsecurity_ctx_t *ctx; + apr_bucket *e, *after; + apr_status_t rv; + + ctx = (ngx_http_modsecurity_ctx_t *) apr_table_get(f->r->notes, NOTE_NGINX_REQUEST_CTX); + if (ctx == NULL) { + return APR_EINVAL; + } + + if (ctx->chain_in == NULL) { + e = apr_bucket_eos_create(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + return APR_SUCCESS; + } + + if (ctx->brigade_in == NULL) { + ctx->brigade_in = ngx_chain_to_apr_brigade(ctx->chain_in, f->r->pool, f->c->bucket_alloc); + if (ctx->brigade_in == NULL) { + return APR_EINVAL; + } + } + + rv = apr_brigade_partition(ctx->brigade_in, readbytes, &after); + if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { + return rv; + } + + e = APR_BRIGADE_FIRST(ctx->brigade_in); + while (e != after) { + if (APR_BUCKET_IS_EOS(e)) { + /* last bucket read, step out of the way */ + ap_remove_input_filter(f); + } + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(bb, e); + e = APR_BRIGADE_FIRST(ctx->brigade_in); + } + + return APR_SUCCESS; +} + +static apr_status_t +ap_nginx_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) { + + ngx_http_modsecurity_ctx_t *ctx; + char *data; + apr_size_t length; + + data = NULL; + + ctx = (ngx_http_modsecurity_ctx_t *) apr_table_get(f->r->notes, NOTE_NGINX_REQUEST_CTX); + if (ctx == NULL) { + return APR_EINVAL; + } + + if (ctx->flatten) { + if (apr_brigade_pflatten(bb, &data, &length, f->r->pool) != APR_SUCCESS) { + goto einval; + } + + ctx->chain_out = ngx_alloc_chain_link(ctx->r->pool); + if (ctx->chain_out == NULL) { + goto einval; + } + ctx->chain_out->next = NULL; + ctx->chain_out->buf = ngx_pcalloc(ctx->r->pool, sizeof(ngx_buf_t)); + if (ctx->chain_out->buf == NULL) { + goto einval; + } + ctx->chain_out->buf->start = ctx->chain_out->buf->pos = (u_char *)data; + ctx->chain_out->buf->end = ctx->chain_out->buf->last = (u_char *)data + length; + goto ok; + } + + ctx->chain_out = apr_brigade_to_ngx_chain(bb, ctx->r->pool); + + if (ctx->chain_out == NULL) { + goto einval; + } +ok: + apr_brigade_destroy(bb); + if (ctx->brigade_in) { + apr_brigade_destroy(ctx->brigade_in); + ctx->brigade_in = NULL; + } + + return APR_SUCCESS; +einval: + apr_brigade_destroy(bb); + if (ctx->brigade_in) { + apr_brigade_destroy(ctx->brigade_in); + ctx->brigade_in = NULL; + } + return APR_EINVAL; +} +#endif 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..b52a649c06 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,14 +166,35 @@ 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; + + bb_in = modsecGetBodyBrigade(f->r); + /* use request brigade */ + if (bb_in) { + 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); + } + + return APR_SUCCESS; + } + + /* call the callback */ if(modsecReadBody == NULL) return AP_NOBODY_READ; @@ -180,11 +202,11 @@ apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *b, 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(b, 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); + APR_BRIGADE_INSERT_TAIL(bb_out, e); } return APR_SUCCESS; @@ -390,6 +412,7 @@ int modsecProcessRequest(request_rec *r) { 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); @@ -404,6 +427,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 @@ -427,12 +460,12 @@ int modsecProcessRequest(request_rec *r) { 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) { diff --git a/standalone/api.h b/standalone/api.h index 825f722cc4..6227d477ab 100644 --- a/standalone/api.h +++ b/standalone/api.h @@ -66,6 +66,29 @@ 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)); From 7921550f302a0b647d397c02542da9dc836d506e Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Tue, 15 Jan 2013 08:33:58 +0800 Subject: [PATCH 02/11] code cleanup --- nginx/modsecurity/ngx_http_modsecurity.c | 234 +---------------------- 1 file changed, 10 insertions(+), 224 deletions(-) diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 56ef06cdc6..a79018fe3f 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -156,10 +156,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; @@ -173,11 +174,8 @@ 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); return NGX_CONF_OK; } @@ -221,11 +219,6 @@ ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) modsecSetLogHook(cf->log, modsecLog); modsecSetDropAction(ngx_http_modsecurity_drop_action); -#if 0 - modsecSetReadBody(modsecurity_read_body_cb); - modsecSetWriteBody(modsecurity_write_body_cb); -#endif - /* ap_hook_insert_filter(ap_nginx_insert_filter, NULL, NULL, APR_HOOK_FIRST); */ modsecInit(); modsecStartConfig(); @@ -300,114 +293,6 @@ ConvertNgxStringToUTF8(ngx_str_t str, apr_pool_t *pool) return t; } -#if 0 -/* -** 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; -} -#endif - 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; @@ -703,8 +588,8 @@ ngx_http_modsecurity_request_body_handler(ngx_http_request_t *r) } bb = ngx_chain_to_apr_brigade(r->request_body->bufs, - ctx->req->pool, - ctx->req->connection->bucket_alloc); + ctx->req->pool, + ctx->req->connection->bucket_alloc); r->request_body = NULL; if (bb == NULL) { return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); @@ -769,7 +654,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"; } @@ -781,6 +666,10 @@ 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); @@ -802,106 +691,3 @@ ngx_http_modsecurity_drop_action(request_rec *r) ctx->r->connection->error = 1; return 0; } - -#if 0 -static apr_status_t -ap_nginx_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, - ap_input_mode_t mode, apr_read_type_e block, - apr_off_t readbytes) { - - ngx_http_modsecurity_ctx_t *ctx; - apr_bucket *e, *after; - apr_status_t rv; - - ctx = (ngx_http_modsecurity_ctx_t *) apr_table_get(f->r->notes, NOTE_NGINX_REQUEST_CTX); - if (ctx == NULL) { - return APR_EINVAL; - } - - if (ctx->chain_in == NULL) { - e = apr_bucket_eos_create(f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, e); - return APR_SUCCESS; - } - - if (ctx->brigade_in == NULL) { - ctx->brigade_in = ngx_chain_to_apr_brigade(ctx->chain_in, f->r->pool, f->c->bucket_alloc); - if (ctx->brigade_in == NULL) { - return APR_EINVAL; - } - } - - rv = apr_brigade_partition(ctx->brigade_in, readbytes, &after); - if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { - return rv; - } - - e = APR_BRIGADE_FIRST(ctx->brigade_in); - while (e != after) { - if (APR_BUCKET_IS_EOS(e)) { - /* last bucket read, step out of the way */ - ap_remove_input_filter(f); - } - APR_BUCKET_REMOVE(e); - APR_BRIGADE_INSERT_TAIL(bb, e); - e = APR_BRIGADE_FIRST(ctx->brigade_in); - } - - return APR_SUCCESS; -} - -static apr_status_t -ap_nginx_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) { - - ngx_http_modsecurity_ctx_t *ctx; - char *data; - apr_size_t length; - - data = NULL; - - ctx = (ngx_http_modsecurity_ctx_t *) apr_table_get(f->r->notes, NOTE_NGINX_REQUEST_CTX); - if (ctx == NULL) { - return APR_EINVAL; - } - - if (ctx->flatten) { - if (apr_brigade_pflatten(bb, &data, &length, f->r->pool) != APR_SUCCESS) { - goto einval; - } - - ctx->chain_out = ngx_alloc_chain_link(ctx->r->pool); - if (ctx->chain_out == NULL) { - goto einval; - } - ctx->chain_out->next = NULL; - ctx->chain_out->buf = ngx_pcalloc(ctx->r->pool, sizeof(ngx_buf_t)); - if (ctx->chain_out->buf == NULL) { - goto einval; - } - ctx->chain_out->buf->start = ctx->chain_out->buf->pos = (u_char *)data; - ctx->chain_out->buf->end = ctx->chain_out->buf->last = (u_char *)data + length; - goto ok; - } - - ctx->chain_out = apr_brigade_to_ngx_chain(bb, ctx->r->pool); - - if (ctx->chain_out == NULL) { - goto einval; - } -ok: - apr_brigade_destroy(bb); - if (ctx->brigade_in) { - apr_brigade_destroy(ctx->brigade_in); - ctx->brigade_in = NULL; - } - - return APR_SUCCESS; -einval: - apr_brigade_destroy(bb); - if (ctx->brigade_in) { - apr_brigade_destroy(ctx->brigade_in); - ctx->brigade_in = NULL; - } - return APR_EINVAL; -} -#endif From cbbbb9c0648290d62716598e554d828471f748ee Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Tue, 15 Jan 2013 08:59:35 +0800 Subject: [PATCH 03/11] Improved split modsecProcessRequest to modsecProcessRequestHeaders and modsecProcessRequestBody --- nginx/modsecurity/ngx_http_modsecurity.c | 56 ++++++++++++------------ standalone/api.c | 8 +++- standalone/api.h | 16 ++++++- 3 files changed, 50 insertions(+), 30 deletions(-) diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index a79018fe3f..c7c41385f5 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -358,6 +358,25 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) } ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); + /* processing request headers */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: process request headers"); + + rc = modsecProcessRequestHeaders(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) { + return rc; + } + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + 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"); @@ -375,26 +394,7 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) 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 = 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) { - return rc; - } - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - } + } return NGX_DECLINED; } @@ -578,14 +578,16 @@ ngx_http_modsecurity_request_body_handler(ngx_http_request_t *r) apr_off_t len; ngx_str_t *str; - rc = NGX_DONE; + 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"); + ngx_http_core_run_phases(r); + ngx_http_finalize_request(r, NGX_DONE); + return; + } - ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: process request body"); - if (ctx == NULL - || r->request_body->bufs == NULL ) { - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); bb = ngx_chain_to_apr_brigade(r->request_body->bufs, ctx->req->pool, @@ -596,7 +598,7 @@ ngx_http_modsecurity_request_body_handler(ngx_http_request_t *r) } modsecSetBodyBrigade(ctx->req, bb); - rc = modsecProcessRequest(ctx->req); + rc = modsecProcessRequestBody(ctx->req); if (rc != DECLINED) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: status: %d, need action", rc); diff --git a/standalone/api.c b/standalone/api.c index b52a649c06..04bbc4c858 100644 --- a/standalone/api.c +++ b/standalone/api.c @@ -407,14 +407,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); @@ -458,6 +461,7 @@ int modsecProcessRequest(request_rec *r) { return status; } + int modsecIsResponseBodyAccessEnabled(request_rec *r) { modsec_rec *msr = retrieve_msr(r); diff --git a/standalone/api.h b/standalone/api.h index 6227d477ab..8516ce2c67 100644 --- a/standalone/api.h +++ b/standalone/api.h @@ -60,7 +60,21 @@ 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); From 1a7ec7317d9b72cd6bfc057801712f3d7c608f9a Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Tue, 15 Jan 2013 09:23:33 +0800 Subject: [PATCH 04/11] Fixed minor error --- nginx/modsecurity/ngx_http_modsecurity.c | 1 + 1 file changed, 1 insertion(+) diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index c7c41385f5..c2e40934da 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -580,6 +580,7 @@ ngx_http_modsecurity_request_body_handler(ngx_http_request_t *r) 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; From e85439a479263e10d757e7290581ad62ee15c99b Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Tue, 15 Jan 2013 09:46:16 +0800 Subject: [PATCH 05/11] Fixed Get request body did not processed --- nginx/modsecurity/ngx_http_modsecurity.c | 55 ++++++++++++++++-------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index c2e40934da..e034321274 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -226,6 +226,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) { @@ -258,6 +261,11 @@ ngx_http_modsecurity_init(ngx_conf_t *cf) } *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; } @@ -362,6 +370,29 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: process request headers"); rc = modsecProcessRequestHeaders(ctx->req); + + 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"); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + 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; + } + /* 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); @@ -376,31 +407,17 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) } return NGX_HTTP_INTERNAL_SERVER_ERROR; } - - 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"); - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (clcf == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - rc = ngx_http_read_client_request_body(r, ngx_http_modsecurity_request_body_handler); - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } + return NGX_DECLINED; +} - return NGX_DONE; +static ngx_int_t +ngx_http_modsecurity_header_filter(ngx_http_request_t *r, ngx_chain_t *in); - } - return NGX_DECLINED; -} #if 0 static ngx_int_t -ngx_http_modsecurity_filter(ngx_http_request_t *r, ngx_chain_t *in) +ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { /* headers */ ngx_http_modsecurity_loc_conf_t *cf; From d47a9996860b6493218cc861f941b74b8996d391 Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Tue, 15 Jan 2013 10:09:12 +0800 Subject: [PATCH 06/11] Fixed compile error --- nginx/modsecurity/config | 1 + nginx/modsecurity/ngx_http_modsecurity.c | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/nginx/modsecurity/config b/nginx/modsecurity/config index c2e6896671..e68a5e9d4a 100644 --- a/nginx/modsecurity/config +++ b/nginx/modsecurity/config @@ -5,3 +5,4 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_modsecurity.c $ngx_addon 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 /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 e034321274..7b729deb61 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -225,10 +225,10 @@ ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) return NGX_OK; } - +#if 0 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_int_t ngx_http_modsecurity_init(ngx_conf_t *cf) { @@ -260,13 +260,12 @@ ngx_http_modsecurity_init(ngx_conf_t *cf) 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; - +#endif return NGX_OK; } @@ -411,11 +410,10 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) return NGX_DECLINED; } +#if 0 static ngx_int_t ngx_http_modsecurity_header_filter(ngx_http_request_t *r, ngx_chain_t *in); - -#if 0 static ngx_int_t ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { From 0e5dbbe71a99c476d8f2296d13994064ec8fdc67 Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Wed, 16 Jan 2013 07:23:18 +0800 Subject: [PATCH 07/11] Fixed ModSecurityConfig/Enable error log --- nginx/modsecurity/ngx_http_modsecurity.c | 1450 +++++++++++----------- 1 file changed, 739 insertions(+), 711 deletions(-) diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 7b729deb61..930d1ea03f 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -1,711 +1,739 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#undef CR -#undef LF -#undef CRLF - -#include "api.h" - -#define NOTE_NGINX_REQUEST_CTX "nginx-ctx" - -typedef struct { - ngx_flag_t enable; - directory_config *config; -} ngx_http_modsecurity_loc_conf_t; - -typedef struct { - ngx_http_request_t *r; - conn_rec *connection; - request_rec *req; -} ngx_http_modsecurity_ctx_t; - - -/* -** 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_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_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 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); -char *ConvertNgxStringToUTF8(ngx_str_t str, apr_pool_t *pool); - -/* command handled by the module */ -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, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - { 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_LOC_CONF_OFFSET, - offsetof(ngx_http_modsecurity_loc_conf_t, enable), - NULL }, - 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 */ - - NULL, /* create main configuration */ - NULL, /* init main configuration */ - - NULL, /* create server configuration */ - NULL, /* merge server configuration */ - - ngx_http_modsecurity_create_loc_conf, /* create location configuration */ - ngx_http_modsecurity_merge_loc_conf /* merge location configuration */ -}; - - -ngx_module_t ngx_http_modsecurity = { - NGX_MODULE_V1, - &ngx_http_modsecurity_ctx, /* module context */ - ngx_http_modsecurity_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - ngx_http_modsecurity_init_process, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - ngx_http_modsecurity_exit_process, /* exit process */ - ngx_http_modsecurity_exit_process, /* exit master */ - 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 * -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_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; -} - -/* merge loc conf */ -static char * -ngx_http_modsecurity_merge_loc_conf(ngx_conf_t *cf, void *parent, - void *child) -{ - ngx_http_modsecurity_loc_conf_t *prev = parent; - ngx_http_modsecurity_loc_conf_t *conf = child; - - ngx_conf_merge_value(conf->enable, prev->enable, 0); - ngx_conf_merge_ptr_value(conf->config, prev->config, NULL); - - return NGX_CONF_OK; -} - -void -modsecLog(void *obj, int level, char *str) -{ - if (obj != NULL) { - level = (level & APLOG_LEVELMASK) + NGX_LOG_EMERG - APLOG_EMERG; - if (level > NGX_LOG_DEBUG) { - level = NGX_LOG_DEBUG; - } - ngx_log_error((ngx_uint_t)level, (ngx_log_t *)obj, 0, "%s", 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; - -void * -modsec_pcre_malloc(size_t size) -{ - return apr_palloc(pool, size); -} - -void -modsec_pcre_free(void *ptr) -{ -} - -static ngx_int_t -ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) -{ - - /* XXX: temporary hack, nginx uses pcre as well and hijacks these two */ - pcre_malloc = modsec_pcre_malloc; - pcre_free = modsec_pcre_free; - - modsecSetLogHook(cf->log, modsecLog); - modsecSetDropAction(ngx_http_modsecurity_drop_action); - - modsecInit(); - modsecStartConfig(); - - return NGX_OK; -} -#if 0 -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_int_t -ngx_http_modsecurity_init(ngx_conf_t *cf) -{ - ngx_http_handler_pt *h; - ngx_http_core_main_conf_t *cmcf; - - modsecFinalizeConfig(); - - cmcf = (ngx_http_core_main_conf_t *) ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); - if (cmcf == NULL) { - return NGX_ERROR; - } - - h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); - if (h == NULL) { - return NGX_ERROR; - } - *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; - 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 - return NGX_OK; -} - - -static ngx_int_t -ngx_http_modsecurity_init_process(ngx_cycle_t *cycle) -{ - modsecSetLogHook(cycle->log, modsecLog); - modsecInitProcess(); - return NGX_OK; -} - -static void -ngx_http_modsecurity_exit_process(ngx_cycle_t *cycle) -{ - modsecTerminate(); -} - - -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; - - return t; -} - -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; - - if(pAddr->sa_family == AF_INET6) { - adrlen = 46; - iplen = 16; - } - - addr->addr_str_len = adrlen; - addr->family = pAddr->sa_family; - - addr->hostname = "unknown"; -#ifdef WIN32 - addr->ipaddr_len = sizeof(IN_ADDR); -#else - addr->ipaddr_len = sizeof(struct in_addr); -#endif - addr->ipaddr_ptr = &addr->sa.sin.sin_addr; - addr->pool = pool; - addr->port = 80; -#ifdef WIN32 - memcpy(&addr->sa.sin.sin_addr.S_un.S_addr, pAddr->sa_data, iplen); -#else - memcpy(&addr->sa.sin.sin_addr.s_addr, pAddr->sa_data, iplen); -#endif - addr->sa.sin.sin_family = pAddr->sa_family; - addr->sa.sin.sin_port = 80; - addr->salen = sizeof(addr->sa); - addr->servname = addr->hostname; - - return addr; -} - - -/* -** [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_core_loc_conf_t *clcf; - ngx_http_modsecurity_ctx_t *ctx; - ngx_int_t rc; - - 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) { - return NGX_DECLINED; - } - - ctx = ngx_http_modsecurity_create_ctx(r); - if (ctx == NULL) { - return NGX_ERROR; - } - ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); - - /* processing request headers */ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: process request headers"); - - rc = modsecProcessRequestHeaders(ctx->req); - - 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"); - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - if (clcf == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - 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; - } - /* 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); - - 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) { - return rc; - } - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - return NGX_DECLINED; -} - -#if 0 -static ngx_int_t -ngx_http_modsecurity_header_filter(ngx_http_request_t *r, ngx_chain_t *in); - -static ngx_int_t -ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) -{ - /* headers */ - 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_http_next_body_filter(r, in); - } - - cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); - - if (!cf->enable) { - return ngx_http_next_body_filter(r, in); - } - - - ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); - if (ctx == NULL) { - ctx = ngx_http_modsecurity_create_ctx(r); - if (ctx == NULL) { - r->filter_finalize = 1; - return NGX_ERROR; - } - ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); - } - - if (ctx->phase < RESPONSE) { - ctx->chain_in = NULL; - if (ngx_list_copy_to_apr_table(&r->headers_out.headers, - ctx->req->headers_out, - ctx->req->pool) != NGX_OK) { - r->filter_finalize = 1; - return NGX_ERROR; - } - ctx->phase = RESPONSE; - } - - /* buffer chain */ - if (in != NULL) { - ngx_chain_t *cl, **ll; - ll = &ctx->chain_in; - - for (cl = ctx->chain_in; cl; cl = cl->next) { - ll = &cl->next; - } - - while (in) { - cl = ngx_alloc_chain_link(pool); - if (cl == NULL) { - r->filter_finalize = 1; - return NGX_ERROR; - } - cl->buf = in->buf; - in = in->next; - cl->next = NULL; - *ll = cl; - ll = &cl->next; - } - - return NGX_AGAIN; - } - - /* ngx chain to apr brigade */ - - - /* process headers and buffered body */ - rc = modsecProcessResponse(ctx->req); - - if (rc == DECLINED) { - /* apr brigade to ngx chain */ - - - return ngx_http_next_body_filter(r, in); - } - - if (rc > NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - - r->filter_finalize = 1; - return NGX_ERROR; -} -#endif - -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; - - 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)); - - ctx = cln->data; - ctx->r = r; - - if (r->connection->requests == 0 || ctx->connection == NULL) { - ctx->connection = modsecNewConnection(); -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 - ctx->connection->remote_addr = CopySockAddr(ctx->connection->pool, r->connection->sockaddr); - ctx->connection->remote_ip = ConvertNgxStringToUTF8(r->connection->addr_text, ctx->connection->pool); -#else - ctx->connection->client_addr = CopySockAddr(ctx->connection->pool, r->connection->sockaddr); - ctx->connection->client_ip = ConvertNgxStringToUTF8(r->connection->addr_text, ctx->connection->pool); -#endif - ctx->connection->remote_host = NULL; - modsecProcessConnection(ctx->connection); - } - - cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); - ctx->req = modsecNewRequest(ctx->connection, cf->config); - ctx->req->request_time = apr_time_now(); - ctx->req->method = ConvertNgxStringToUTF8(r->method_name, ctx->req->pool); - ctx->req->path_info = ConvertNgxStringToUTF8(r->unparsed_uri, ctx->req->pool); - ctx->req->unparsed_uri = ConvertNgxStringToUTF8(r->unparsed_uri, ctx->req->pool); - ctx->req->uri = ctx->req->unparsed_uri; - ctx->req->the_request = ConvertNgxStringToUTF8(r->request_line, ctx->req->pool); - ctx->req->args = ConvertNgxStringToUTF8(r->args, ctx->req->pool); - ctx->req->filename = ctx->req->path_info; - - ctx->req->parsed_uri.scheme = "http"; - ctx->req->parsed_uri.path = ctx->req->path_info; - ctx->req->parsed_uri.is_initialized = 1; - ctx->req->parsed_uri.port = 80; - ctx->req->parsed_uri.port_str = "80"; - ctx->req->parsed_uri.query = ctx->req->args; - ctx->req->parsed_uri.dns_looked_up = 0; - ctx->req->parsed_uri.dns_resolved = 0; - ctx->req->parsed_uri.password = NULL; - ctx->req->parsed_uri.user = NULL; - ctx->req->parsed_uri.fragment = ConvertNgxStringToUTF8(r->exten, ctx->req->pool); - - if (ngx_list_copy_to_apr_table(&r->headers_in.headers, - ctx->req->headers_in, - ctx->req->pool) != NGX_OK) { - return NULL; - } - - /* 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); - return ctx; -} - -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; - apr_bucket_brigade *bb; - ngx_int_t rc; - apr_off_t len; - ngx_str_t *str; - - 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); - - bb = ngx_chain_to_apr_brigade(r->request_body->bufs, - ctx->req->pool, - ctx->req->connection->bucket_alloc); - r->request_body = NULL; - if (bb == NULL) { - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - modsecSetBodyBrigade(ctx->req, bb); - rc = modsecProcessRequestBody(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 >= 600) { - rc = NGX_HTTP_INTERNAL_SERVER_ERROR; - } - return ngx_http_finalize_request(r, rc); - } - bb = modsecGetBodyBrigade(ctx->req); - - if (apr_brigade_length(bb, 1, &len) != APR_SUCCESS) { - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - ngx_buf_t *buf = ngx_create_temp_buf(ctx->r->pool, (size_t) len); - if (buf == NULL){ - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - if (apr_brigade_flatten(bb, (char *)buf->pos, (apr_size_t *)&len) != APR_SUCCESS) { - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - apr_brigade_cleanup(bb); - - buf->last += len; - r->header_in = buf; - r->headers_in.content_length_n = len; - - /* 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_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - str->len = ngx_snprintf(str->data, NGX_OFF_T_LEN, "%O", len) - str->data; - - 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) -{ - ngx_http_modsecurity_loc_conf_t *mscf = conf; - ngx_str_t *value; - const char *msg; - - if (mscf->config != NGX_CONF_UNSET_PTR) { - return "is duplicate"; - } - - value = cf->args->elts; - - if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) { - return NGX_CONF_ERROR; - } - - 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); - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; -} - -static int -ngx_http_modsecurity_drop_action(request_rec *r) -{ - 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 -1; - } - ctx->r->connection->error = 1; - return 0; -} +/* +* ModSecurity for Apache 2.x, http://www.modsecurity.org/ +* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* +* You may not use this file except in compliance with +* the License.  You may obtain a copy of the License at +* +*     http://www.apache.org/licenses/LICENSE-2.0 +* +* If any of the files related to licensing are missing or if you have any +* other questions related to licensing please contact Trustwave Holdings, Inc. +* directly using the email address security@modsecurity.org. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef CR +#undef LF +#undef CRLF + +#include "api.h" + +#define NOTE_NGINX_REQUEST_CTX "nginx-ctx" + +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_http_modsecurity_ctx_t; + + +/* +** 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_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_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); +static char *ngx_http_modsecurity_enable(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 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); +char *ConvertNgxStringToUTF8(ngx_str_t str, apr_pool_t *pool); + +/* command handled by the module */ +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, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { 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_http_modsecurity_enable, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_modsecurity_loc_conf_t, enable), + NULL }, + 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 */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_modsecurity_create_loc_conf, /* create location configuration */ + ngx_http_modsecurity_merge_loc_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_modsecurity = { + NGX_MODULE_V1, + &ngx_http_modsecurity_ctx, /* module context */ + ngx_http_modsecurity_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + ngx_http_modsecurity_init_process, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + ngx_http_modsecurity_exit_process, /* exit process */ + ngx_http_modsecurity_exit_process, /* exit master */ + 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 * +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_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; +} + +/* merge loc conf */ +static char * +ngx_http_modsecurity_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child) +{ + ngx_http_modsecurity_loc_conf_t *prev = parent; + ngx_http_modsecurity_loc_conf_t *conf = child; + + 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 +modsecLog(void *obj, int level, char *str) +{ + if (obj != NULL) { + level = (level & APLOG_LEVELMASK) + NGX_LOG_EMERG - APLOG_EMERG; + if (level > NGX_LOG_DEBUG) { + level = NGX_LOG_DEBUG; + } + ngx_log_error((ngx_uint_t)level, (ngx_log_t *)obj, 0, "%s", 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; + +void * +modsec_pcre_malloc(size_t size) +{ + return apr_palloc(pool, size); +} + +void +modsec_pcre_free(void *ptr) +{ +} + +static ngx_int_t +ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) +{ + + /* XXX: temporary hack, nginx uses pcre as well and hijacks these two */ + pcre_malloc = modsec_pcre_malloc; + pcre_free = modsec_pcre_free; + + modsecSetLogHook(cf->log, modsecLog); + modsecSetDropAction(ngx_http_modsecurity_drop_action); + + modsecInit(); + modsecStartConfig(); + + return NGX_OK; +} +#if 0 +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_int_t +ngx_http_modsecurity_init(ngx_conf_t *cf) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + modsecFinalizeConfig(); + + cmcf = (ngx_http_core_main_conf_t *) ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + if (cmcf == NULL) { + return NGX_ERROR; + } + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + *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; + 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 + return NGX_OK; +} + + +static ngx_int_t +ngx_http_modsecurity_init_process(ngx_cycle_t *cycle) +{ + modsecSetLogHook(cycle->log, modsecLog); + modsecInitProcess(); + return NGX_OK; +} + +static void +ngx_http_modsecurity_exit_process(ngx_cycle_t *cycle) +{ + modsecTerminate(); +} + + +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; + + return t; +} + +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; + + if(pAddr->sa_family == AF_INET6) { + adrlen = 46; + iplen = 16; + } + + addr->addr_str_len = adrlen; + addr->family = pAddr->sa_family; + + addr->hostname = "unknown"; +#ifdef WIN32 + addr->ipaddr_len = sizeof(IN_ADDR); +#else + addr->ipaddr_len = sizeof(struct in_addr); +#endif + addr->ipaddr_ptr = &addr->sa.sin.sin_addr; + addr->pool = pool; + addr->port = 80; +#ifdef WIN32 + memcpy(&addr->sa.sin.sin_addr.S_un.S_addr, pAddr->sa_data, iplen); +#else + memcpy(&addr->sa.sin.sin_addr.s_addr, pAddr->sa_data, iplen); +#endif + addr->sa.sin.sin_family = pAddr->sa_family; + addr->sa.sin.sin_port = 80; + addr->salen = sizeof(addr->sa); + addr->servname = addr->hostname; + + return addr; +} + + +/* +** [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_core_loc_conf_t *clcf; + ngx_http_modsecurity_ctx_t *ctx; + ngx_int_t rc; + + 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) { + return NGX_DECLINED; + } + + ctx = ngx_http_modsecurity_create_ctx(r); + if (ctx == NULL) { + return NGX_ERROR; + } + ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); + + /* processing request headers */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: process request headers"); + + rc = modsecProcessRequestHeaders(ctx->req); + + 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"); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + 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; + } + /* 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); + + 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) { + return rc; + } + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_DECLINED; +} + +#if 0 +static ngx_int_t +ngx_http_modsecurity_header_filter(ngx_http_request_t *r, ngx_chain_t *in); + +static ngx_int_t +ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + /* headers */ + 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_http_next_body_filter(r, in); + } + + cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); + + if (!cf->enable) { + return ngx_http_next_body_filter(r, in); + } + + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + if (ctx == NULL) { + ctx = ngx_http_modsecurity_create_ctx(r); + if (ctx == NULL) { + r->filter_finalize = 1; + return NGX_ERROR; + } + ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); + } + + if (ctx->phase < RESPONSE) { + ctx->chain_in = NULL; + if (ngx_list_copy_to_apr_table(&r->headers_out.headers, + ctx->req->headers_out, + ctx->req->pool) != NGX_OK) { + r->filter_finalize = 1; + return NGX_ERROR; + } + ctx->phase = RESPONSE; + } + + /* buffer chain */ + if (in != NULL) { + ngx_chain_t *cl, **ll; + ll = &ctx->chain_in; + + for (cl = ctx->chain_in; cl; cl = cl->next) { + ll = &cl->next; + } + + while (in) { + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + r->filter_finalize = 1; + return NGX_ERROR; + } + cl->buf = in->buf; + in = in->next; + cl->next = NULL; + *ll = cl; + ll = &cl->next; + } + + return NGX_AGAIN; + } + + /* ngx chain to apr brigade */ + + + /* process headers and buffered body */ + rc = modsecProcessResponse(ctx->req); + + if (rc == DECLINED) { + /* apr brigade to ngx chain */ + + + return ngx_http_next_body_filter(r, in); + } + + if (rc > NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + r->filter_finalize = 1; + return NGX_ERROR; +} +#endif + +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; + + 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)); + + ctx = cln->data; + ctx->r = r; + + if (r->connection->requests == 0 || ctx->connection == NULL) { + ctx->connection = modsecNewConnection(); +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 + ctx->connection->remote_addr = CopySockAddr(ctx->connection->pool, r->connection->sockaddr); + ctx->connection->remote_ip = ConvertNgxStringToUTF8(r->connection->addr_text, ctx->connection->pool); +#else + ctx->connection->client_addr = CopySockAddr(ctx->connection->pool, r->connection->sockaddr); + ctx->connection->client_ip = ConvertNgxStringToUTF8(r->connection->addr_text, ctx->connection->pool); +#endif + ctx->connection->remote_host = NULL; + modsecProcessConnection(ctx->connection); + } + + cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); + ctx->req = modsecNewRequest(ctx->connection, cf->config); + ctx->req->request_time = apr_time_now(); + ctx->req->method = ConvertNgxStringToUTF8(r->method_name, ctx->req->pool); + ctx->req->path_info = ConvertNgxStringToUTF8(r->unparsed_uri, ctx->req->pool); + ctx->req->unparsed_uri = ConvertNgxStringToUTF8(r->unparsed_uri, ctx->req->pool); + ctx->req->uri = ctx->req->unparsed_uri; + ctx->req->the_request = ConvertNgxStringToUTF8(r->request_line, ctx->req->pool); + ctx->req->args = ConvertNgxStringToUTF8(r->args, ctx->req->pool); + ctx->req->filename = ctx->req->path_info; + + ctx->req->parsed_uri.scheme = "http"; + ctx->req->parsed_uri.path = ctx->req->path_info; + ctx->req->parsed_uri.is_initialized = 1; + ctx->req->parsed_uri.port = 80; + ctx->req->parsed_uri.port_str = "80"; + ctx->req->parsed_uri.query = ctx->req->args; + ctx->req->parsed_uri.dns_looked_up = 0; + ctx->req->parsed_uri.dns_resolved = 0; + ctx->req->parsed_uri.password = NULL; + ctx->req->parsed_uri.user = NULL; + ctx->req->parsed_uri.fragment = ConvertNgxStringToUTF8(r->exten, ctx->req->pool); + + if (ngx_list_copy_to_apr_table(&r->headers_in.headers, + ctx->req->headers_in, + ctx->req->pool) != NGX_OK) { + return NULL; + } + + /* 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); + return ctx; +} + +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; + apr_bucket_brigade *bb; + ngx_int_t rc; + apr_off_t len; + ngx_str_t *str; + + 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); + + bb = ngx_chain_to_apr_brigade(r->request_body->bufs, + ctx->req->pool, + ctx->req->connection->bucket_alloc); + r->request_body = NULL; + if (bb == NULL) { + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + modsecSetBodyBrigade(ctx->req, bb); + rc = modsecProcessRequestBody(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 >= 600) { + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + } + return ngx_http_finalize_request(r, rc); + } + bb = modsecGetBodyBrigade(ctx->req); + + if (apr_brigade_length(bb, 1, &len) != APR_SUCCESS) { + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + ngx_buf_t *buf = ngx_create_temp_buf(ctx->r->pool, (size_t) len); + if (buf == NULL){ + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + if (apr_brigade_flatten(bb, (char *)buf->pos, (apr_size_t *)&len) != APR_SUCCESS) { + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + apr_brigade_cleanup(bb); + + buf->last += len; + r->header_in = buf; + r->headers_in.content_length_n = len; + + /* 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_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + str->len = ngx_snprintf(str->data, NGX_OFF_T_LEN, "%O", len) - str->data; + + 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) +{ + ngx_http_modsecurity_loc_conf_t *mscf = conf; + ngx_str_t *value; + const char *msg; + + if (mscf->config != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + 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_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) +{ + 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 -1; + } + ctx->r->connection->error = 1; + return 0; +} From fadba24dc123f1a69cdd2f76ccc6b272e49f6c3a Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Wed, 16 Jan 2013 08:59:09 +0800 Subject: [PATCH 08/11] Added response phase --- nginx/modsecurity/apr_bucket_nginx.c | 159 ++- nginx/modsecurity/apr_bucket_nginx.h | 12 +- nginx/modsecurity/config | 2 +- nginx/modsecurity/ngx_http_modsecurity.c | 1485 +++++++++++----------- standalone/api.c | 111 +- 5 files changed, 909 insertions(+), 860 deletions(-) diff --git a/nginx/modsecurity/apr_bucket_nginx.c b/nginx/modsecurity/apr_bucket_nginx.c index f4763c69da..3bafb30384 100644 --- a/nginx/modsecurity/apr_bucket_nginx.c +++ b/nginx/modsecurity/apr_bucket_nginx.c @@ -1,5 +1,4 @@ -#include #include static apr_status_t nginx_bucket_read(apr_bucket *b, const char **str, @@ -93,117 +92,141 @@ static void nginx_bucket_destroy(void *data) } ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool) { - ngx_buf_t *buf; + ngx_buf_t *buf, *b; apr_bucket_nginx *n; ngx_uint_t len; u_char *data; - if (e->type == &apr_bucket_type_nginx) { - n = e->data; - - buf = ngx_alloc_buf(pool); - if (buf == NULL) { - return NULL; - } - - ngx_memcpy(buf, n->buf, 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; - } - return buf; - } - - /* convert apr_bucket to ngx_buf_t */ + 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) { - /* TODO: may be we can deal more bucket type here */ - /* shadow copy */ 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; } -apr_bucket_brigade * -ngx_chain_to_apr_brigade(ngx_chain_t *chain, apr_pool_t *p, apr_bucket_alloc_t *list) { - apr_bucket_brigade *b; +ngx_int_t +move_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *pool) { apr_bucket *e; - - b = apr_brigade_create(p, list); - + ngx_chain_t *cl; + while (chain) { - e = ngx_buf_to_apr_bucket(chain->buf, p, list); - APR_BRIGADE_INSERT_TAIL(b, e); + 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); } - e = apr_bucket_eos_create(list); - APR_BRIGADE_INSERT_TAIL(b, e); - - return b; + return NGX_AGAIN; } -/* apr_brigade_destory could be called after converted */ -ngx_chain_t * -apr_brigade_to_ngx_chain(apr_bucket_brigade *b, ngx_pool_t *pool) { +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, *tl, **ll; + ngx_chain_t *cl; cl = NULL; - ll = &cl; - - for(e = APR_BRIGADE_FIRST(b); - e != APR_BRIGADE_SENTINEL(b); + + 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)) { - buf = apr_bucket_to_ngx_buf(e, pool); - if (buf == NULL) { - return NULL; + + if (APR_BUCKET_IS_EOS(e)) { + if (cl == NULL) { + *ll = cl; + } else { + cl->buf->last_buf = 1; + } + apr_brigade_cleanup(bb); + return NGX_OK; } - tl = ngx_alloc_chain_link(pool); - if (tl == NULL) { - return NULL; + + if (APR_BUCKET_IS_METADATA(e)) { + continue; } - tl->buf = buf; - tl->next = NULL; - *ll = tl; - ll = &tl->next; - } - - if (cl == NULL) { + buf = apr_bucket_to_ngx_buf(e, pool); + if (buf == NULL) { + break; + } + cl = ngx_alloc_chain_link(pool); if (cl == NULL) { - return NULL; + break; } - - cl->buf = ngx_calloc_buf(pool); - if (cl->buf == NULL) { - return NULL; - } - + + cl->buf = buf; cl->next = NULL; + *ll = cl; + ll = &cl->next; } - return cl; + 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 index 2a9f4c6adb..4dd1ca0ff1 100644 --- a/nginx/modsecurity/apr_bucket_nginx.h +++ b/nginx/modsecurity/apr_bucket_nginx.h @@ -1,4 +1,4 @@ - +#pragma once #include #include "apr_buckets.h" @@ -13,12 +13,6 @@ apr_bucket * apr_bucket_nginx_make(apr_bucket *e, ngx_buf_t *buf, ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool); - -apr_bucket_brigade * -ngx_chain_to_apr_brigade(ngx_chain_t *chain, apr_pool_t *p, apr_bucket_alloc_t *list); - - -/* apr_brigade_destory could be called after converted */ -ngx_chain_t * -apr_brigade_to_ngx_chain(apr_bucket_brigade *b, 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 e68a5e9d4a..ecd8aa90f7 100644 --- a/nginx/modsecurity/config +++ b/nginx/modsecurity/config @@ -1,6 +1,6 @@ ngx_addon_name=ngx_http_modsecurity # HTTP_MODULES="$HTTP_MODULES ngx_http_modsecurity" -HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_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" diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 930d1ea03f..1dee180ed7 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -1,739 +1,746 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#undef CR -#undef LF -#undef CRLF - -#include "api.h" - -#define NOTE_NGINX_REQUEST_CTX "nginx-ctx" - -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_http_modsecurity_ctx_t; - - -/* -** 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_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_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); -static char *ngx_http_modsecurity_enable(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 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); -char *ConvertNgxStringToUTF8(ngx_str_t str, apr_pool_t *pool); - -/* command handled by the module */ -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, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - { 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_http_modsecurity_enable, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_modsecurity_loc_conf_t, enable), - NULL }, - 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 */ - - NULL, /* create main configuration */ - NULL, /* init main configuration */ - - NULL, /* create server configuration */ - NULL, /* merge server configuration */ - - ngx_http_modsecurity_create_loc_conf, /* create location configuration */ - ngx_http_modsecurity_merge_loc_conf /* merge location configuration */ -}; - - -ngx_module_t ngx_http_modsecurity = { - NGX_MODULE_V1, - &ngx_http_modsecurity_ctx, /* module context */ - ngx_http_modsecurity_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - ngx_http_modsecurity_init_process, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - ngx_http_modsecurity_exit_process, /* exit process */ - ngx_http_modsecurity_exit_process, /* exit master */ - 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 * -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_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; -} - -/* merge loc conf */ -static char * -ngx_http_modsecurity_merge_loc_conf(ngx_conf_t *cf, void *parent, - void *child) -{ - ngx_http_modsecurity_loc_conf_t *prev = parent; - ngx_http_modsecurity_loc_conf_t *conf = child; - - 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 -modsecLog(void *obj, int level, char *str) -{ - if (obj != NULL) { - level = (level & APLOG_LEVELMASK) + NGX_LOG_EMERG - APLOG_EMERG; - if (level > NGX_LOG_DEBUG) { - level = NGX_LOG_DEBUG; - } - ngx_log_error((ngx_uint_t)level, (ngx_log_t *)obj, 0, "%s", 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; - -void * -modsec_pcre_malloc(size_t size) -{ - return apr_palloc(pool, size); -} - -void -modsec_pcre_free(void *ptr) -{ -} - -static ngx_int_t -ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) -{ - - /* XXX: temporary hack, nginx uses pcre as well and hijacks these two */ - pcre_malloc = modsec_pcre_malloc; - pcre_free = modsec_pcre_free; - - modsecSetLogHook(cf->log, modsecLog); - modsecSetDropAction(ngx_http_modsecurity_drop_action); - - modsecInit(); - modsecStartConfig(); - - return NGX_OK; -} -#if 0 -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_int_t -ngx_http_modsecurity_init(ngx_conf_t *cf) -{ - ngx_http_handler_pt *h; - ngx_http_core_main_conf_t *cmcf; - - modsecFinalizeConfig(); - - cmcf = (ngx_http_core_main_conf_t *) ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); - if (cmcf == NULL) { - return NGX_ERROR; - } - - h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); - if (h == NULL) { - return NGX_ERROR; - } - *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; - 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 - return NGX_OK; -} - - -static ngx_int_t -ngx_http_modsecurity_init_process(ngx_cycle_t *cycle) -{ - modsecSetLogHook(cycle->log, modsecLog); - modsecInitProcess(); - return NGX_OK; -} - -static void -ngx_http_modsecurity_exit_process(ngx_cycle_t *cycle) -{ - modsecTerminate(); -} - - -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; - - return t; -} - -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; - - if(pAddr->sa_family == AF_INET6) { - adrlen = 46; - iplen = 16; - } - - addr->addr_str_len = adrlen; - addr->family = pAddr->sa_family; - - addr->hostname = "unknown"; -#ifdef WIN32 - addr->ipaddr_len = sizeof(IN_ADDR); -#else - addr->ipaddr_len = sizeof(struct in_addr); -#endif - addr->ipaddr_ptr = &addr->sa.sin.sin_addr; - addr->pool = pool; - addr->port = 80; -#ifdef WIN32 - memcpy(&addr->sa.sin.sin_addr.S_un.S_addr, pAddr->sa_data, iplen); -#else - memcpy(&addr->sa.sin.sin_addr.s_addr, pAddr->sa_data, iplen); -#endif - addr->sa.sin.sin_family = pAddr->sa_family; - addr->sa.sin.sin_port = 80; - addr->salen = sizeof(addr->sa); - addr->servname = addr->hostname; - - return addr; -} - - -/* -** [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_core_loc_conf_t *clcf; - ngx_http_modsecurity_ctx_t *ctx; - ngx_int_t rc; - - 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) { - return NGX_DECLINED; - } - - ctx = ngx_http_modsecurity_create_ctx(r); - if (ctx == NULL) { - return NGX_ERROR; - } - ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); - - /* processing request headers */ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: process request headers"); - - rc = modsecProcessRequestHeaders(ctx->req); - - 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"); - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - if (clcf == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - 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; - } - /* 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); - - 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) { - return rc; - } - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - return NGX_DECLINED; -} - -#if 0 -static ngx_int_t -ngx_http_modsecurity_header_filter(ngx_http_request_t *r, ngx_chain_t *in); - -static ngx_int_t -ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) -{ - /* headers */ - 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_http_next_body_filter(r, in); - } - - cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); - - if (!cf->enable) { - return ngx_http_next_body_filter(r, in); - } - - - ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); - if (ctx == NULL) { - ctx = ngx_http_modsecurity_create_ctx(r); - if (ctx == NULL) { - r->filter_finalize = 1; - return NGX_ERROR; - } - ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); - } - - if (ctx->phase < RESPONSE) { - ctx->chain_in = NULL; - if (ngx_list_copy_to_apr_table(&r->headers_out.headers, - ctx->req->headers_out, - ctx->req->pool) != NGX_OK) { - r->filter_finalize = 1; - return NGX_ERROR; - } - ctx->phase = RESPONSE; - } - - /* buffer chain */ - if (in != NULL) { - ngx_chain_t *cl, **ll; - ll = &ctx->chain_in; - - for (cl = ctx->chain_in; cl; cl = cl->next) { - ll = &cl->next; - } - - while (in) { - cl = ngx_alloc_chain_link(pool); - if (cl == NULL) { - r->filter_finalize = 1; - return NGX_ERROR; - } - cl->buf = in->buf; - in = in->next; - cl->next = NULL; - *ll = cl; - ll = &cl->next; - } - - return NGX_AGAIN; - } - - /* ngx chain to apr brigade */ - - - /* process headers and buffered body */ - rc = modsecProcessResponse(ctx->req); - - if (rc == DECLINED) { - /* apr brigade to ngx chain */ - - - return ngx_http_next_body_filter(r, in); - } - - if (rc > NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - - r->filter_finalize = 1; - return NGX_ERROR; -} -#endif - -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; - - 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)); - - ctx = cln->data; - ctx->r = r; - - if (r->connection->requests == 0 || ctx->connection == NULL) { - ctx->connection = modsecNewConnection(); -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 - ctx->connection->remote_addr = CopySockAddr(ctx->connection->pool, r->connection->sockaddr); - ctx->connection->remote_ip = ConvertNgxStringToUTF8(r->connection->addr_text, ctx->connection->pool); -#else - ctx->connection->client_addr = CopySockAddr(ctx->connection->pool, r->connection->sockaddr); - ctx->connection->client_ip = ConvertNgxStringToUTF8(r->connection->addr_text, ctx->connection->pool); -#endif - ctx->connection->remote_host = NULL; - modsecProcessConnection(ctx->connection); - } - - cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); - ctx->req = modsecNewRequest(ctx->connection, cf->config); - ctx->req->request_time = apr_time_now(); - ctx->req->method = ConvertNgxStringToUTF8(r->method_name, ctx->req->pool); - ctx->req->path_info = ConvertNgxStringToUTF8(r->unparsed_uri, ctx->req->pool); - ctx->req->unparsed_uri = ConvertNgxStringToUTF8(r->unparsed_uri, ctx->req->pool); - ctx->req->uri = ctx->req->unparsed_uri; - ctx->req->the_request = ConvertNgxStringToUTF8(r->request_line, ctx->req->pool); - ctx->req->args = ConvertNgxStringToUTF8(r->args, ctx->req->pool); - ctx->req->filename = ctx->req->path_info; - - ctx->req->parsed_uri.scheme = "http"; - ctx->req->parsed_uri.path = ctx->req->path_info; - ctx->req->parsed_uri.is_initialized = 1; - ctx->req->parsed_uri.port = 80; - ctx->req->parsed_uri.port_str = "80"; - ctx->req->parsed_uri.query = ctx->req->args; - ctx->req->parsed_uri.dns_looked_up = 0; - ctx->req->parsed_uri.dns_resolved = 0; - ctx->req->parsed_uri.password = NULL; - ctx->req->parsed_uri.user = NULL; - ctx->req->parsed_uri.fragment = ConvertNgxStringToUTF8(r->exten, ctx->req->pool); - - if (ngx_list_copy_to_apr_table(&r->headers_in.headers, - ctx->req->headers_in, - ctx->req->pool) != NGX_OK) { - return NULL; - } - - /* 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); - return ctx; -} - -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; - apr_bucket_brigade *bb; - ngx_int_t rc; - apr_off_t len; - ngx_str_t *str; - - 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); - - bb = ngx_chain_to_apr_brigade(r->request_body->bufs, - ctx->req->pool, - ctx->req->connection->bucket_alloc); - r->request_body = NULL; - if (bb == NULL) { - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - modsecSetBodyBrigade(ctx->req, bb); - rc = modsecProcessRequestBody(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 >= 600) { - rc = NGX_HTTP_INTERNAL_SERVER_ERROR; - } - return ngx_http_finalize_request(r, rc); - } - bb = modsecGetBodyBrigade(ctx->req); - - if (apr_brigade_length(bb, 1, &len) != APR_SUCCESS) { - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - ngx_buf_t *buf = ngx_create_temp_buf(ctx->r->pool, (size_t) len); - if (buf == NULL){ - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - if (apr_brigade_flatten(bb, (char *)buf->pos, (apr_size_t *)&len) != APR_SUCCESS) { - return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - apr_brigade_cleanup(bb); - - buf->last += len; - r->header_in = buf; - r->headers_in.content_length_n = len; - - /* 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_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - str->len = ngx_snprintf(str->data, NGX_OFF_T_LEN, "%O", len) - str->data; - - 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) -{ - ngx_http_modsecurity_loc_conf_t *mscf = conf; - ngx_str_t *value; - const char *msg; - - if (mscf->config != NGX_CONF_UNSET_PTR) { - return "is duplicate"; - } - - value = cf->args->elts; - - if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) { - return NGX_CONF_ERROR; - } - - 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_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) -{ - 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 -1; - } - ctx->r->connection->error = 1; - return 0; -} +/* +* ModSecurity for Apache 2.x, http://www.modsecurity.org/ +* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* +* You may not use this file except in compliance with +* the License.  You may obtain a copy of the License at +* +*     http://www.apache.org/licenses/LICENSE-2.0 +* +* If any of the files related to licensing are missing or if you have any +* other questions related to licensing please contact Trustwave Holdings, Inc. +* directly using the email address security@modsecurity.org. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef CR +#undef LF +#undef CRLF + +#include "api.h" + +#define NOTE_NGINX_REQUEST_CTX "nginx-ctx" + +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; + + apr_bucket_brigade *brigade; + unsigned complete; + unsigned blocked; +} ngx_http_modsecurity_ctx_t; + + +/* +** 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 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); +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); +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[] = { + { ngx_string("ModSecurityConfig"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_modsecurity_config, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { 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_http_modsecurity_enable, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_modsecurity_loc_conf_t, enable), + NULL }, + 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 */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_modsecurity_create_loc_conf, /* create location configuration */ + ngx_http_modsecurity_merge_loc_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_modsecurity = { + NGX_MODULE_V1, + &ngx_http_modsecurity_ctx, /* module context */ + ngx_http_modsecurity_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + ngx_http_modsecurity_init_process, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + ngx_http_modsecurity_exit_process, /* exit process */ + ngx_http_modsecurity_exit_process, /* exit master */ + 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 * +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_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; +} + +/* merge loc conf */ +static char * +ngx_http_modsecurity_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child) +{ + ngx_http_modsecurity_loc_conf_t *prev = parent; + ngx_http_modsecurity_loc_conf_t *conf = child; + + 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; +} + +static void +modsecLog(void *obj, int level, char *str) +{ + if (obj != NULL) { + level = (level & APLOG_LEVELMASK) + NGX_LOG_EMERG - APLOG_EMERG; + if (level > NGX_LOG_DEBUG) { + level = NGX_LOG_DEBUG; + } + ngx_log_error((ngx_uint_t)level, (ngx_log_t *)obj, 0, "%s", 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 ngx_int_t +ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) +{ + + /* XXX: temporary hack, nginx uses pcre as well and hijacks these two */ + pcre_malloc = modsec_pcre_malloc; + pcre_free = modsec_pcre_free; + + modsecSetLogHook(cf->log, modsecLog); + modsecSetDropAction(ngx_http_modsecurity_drop_action); + + modsecInit(); + modsecStartConfig(); + + 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) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + modsecFinalizeConfig(); + + cmcf = (ngx_http_core_main_conf_t *) ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + if (cmcf == NULL) { + return NGX_ERROR; + } + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + *h = ngx_http_modsecurity_handler; + + 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; +} + + +static ngx_int_t +ngx_http_modsecurity_init_process(ngx_cycle_t *cycle) +{ + modsecSetLogHook(cycle->log, modsecLog); + modsecInitProcess(); + return NGX_OK; +} + +static void +ngx_http_modsecurity_exit_process(ngx_cycle_t *cycle) +{ + modsecTerminate(); +} + + +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; + + return t; +} + +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; + + if(pAddr->sa_family == AF_INET6) { + adrlen = 46; + iplen = 16; + } + + addr->addr_str_len = adrlen; + addr->family = pAddr->sa_family; + + addr->hostname = "unknown"; +#ifdef WIN32 + addr->ipaddr_len = sizeof(IN_ADDR); +#else + addr->ipaddr_len = sizeof(struct in_addr); +#endif + addr->ipaddr_ptr = &addr->sa.sin.sin_addr; + addr->pool = pool; + addr->port = 80; +#ifdef WIN32 + memcpy(&addr->sa.sin.sin_addr.S_un.S_addr, pAddr->sa_data, iplen); +#else + memcpy(&addr->sa.sin.sin_addr.s_addr, pAddr->sa_data, iplen); +#endif + addr->sa.sin.sin_family = pAddr->sa_family; + addr->sa.sin.sin_port = 80; + addr->salen = sizeof(addr->sa); + addr->servname = addr->hostname; + + return addr; +} + + +/* +** [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_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: handler"); + + cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); + + /* Process only main request */ + if (r != r->main || r->internal || !cf->enable) { + return NGX_DECLINED; + } + + ctx = ngx_http_modsecurity_create_ctx(r); + if (ctx == NULL) { + return NGX_ERROR; + } + ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); + + /* processing request headers */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: process request headers"); + + rc = modsecProcessRequestHeaders(ctx->req); + + 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"); + + 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; + } + + return NGX_DECLINED; +} + + +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); + + 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) { + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + } + return ngx_http_finalize_request(r, 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); + + 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; + } + + 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 = move_brigade_to_chain(ctx->brigade, &in, ctx->r->pool); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + 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); + + if (rc == NGX_ERROR || rc > NGX_OK) { + return rc; + } + + 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 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; + 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; + cln->data = ctx; + + ctx->r = r; + + if (r->connection->requests == 0 || ctx->connection == NULL) { + ctx->connection = modsecNewConnection(); +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 + ctx->connection->remote_addr = CopySockAddr(ctx->connection->pool, r->connection->sockaddr); + ctx->connection->remote_ip = ConvertNgxStringToUTF8(r->connection->addr_text, ctx->connection->pool); +#else + ctx->connection->client_addr = CopySockAddr(ctx->connection->pool, r->connection->sockaddr); + ctx->connection->client_ip = ConvertNgxStringToUTF8(r->connection->addr_text, ctx->connection->pool); +#endif + ctx->connection->remote_host = NULL; + modsecProcessConnection(ctx->connection); + } + + cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); + ctx->req = modsecNewRequest(ctx->connection, cf->config); + ctx->req->request_time = apr_time_now(); + ctx->req->method = ConvertNgxStringToUTF8(r->method_name, ctx->req->pool); + ctx->req->path_info = ConvertNgxStringToUTF8(r->unparsed_uri, ctx->req->pool); + ctx->req->unparsed_uri = ConvertNgxStringToUTF8(r->unparsed_uri, ctx->req->pool); + ctx->req->uri = ctx->req->unparsed_uri; + ctx->req->the_request = ConvertNgxStringToUTF8(r->request_line, ctx->req->pool); + ctx->req->args = ConvertNgxStringToUTF8(r->args, ctx->req->pool); + ctx->req->filename = ctx->req->path_info; + + ctx->req->parsed_uri.scheme = "http"; + ctx->req->parsed_uri.path = ctx->req->path_info; + ctx->req->parsed_uri.is_initialized = 1; + ctx->req->parsed_uri.port = 80; + ctx->req->parsed_uri.port_str = "80"; + ctx->req->parsed_uri.query = ctx->req->args; + ctx->req->parsed_uri.dns_looked_up = 0; + ctx->req->parsed_uri.dns_resolved = 0; + ctx->req->parsed_uri.password = NULL; + ctx->req->parsed_uri.user = NULL; + ctx->req->parsed_uri.fragment = ConvertNgxStringToUTF8(r->exten, ctx->req->pool); + + if (ngx_list_copy_to_apr_table(&r->headers_in.headers, + ctx->req->headers_in, + ctx->req->pool) != NGX_OK) { + return NULL; + } + + /* XXX: if mod_uniqid enabled - use it's value */ + apr_table_setn(ctx->req->subprocess_env, "UNIQUE_ID", "12345"); + + 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 *)); + + *(const char **)apr_array_push(ctx->req->content_languages) = lang; + } + + ctx->brigade = apr_brigade_create(ctx->req->pool, ctx->req->connection->bucket_alloc); + + if (ctx->brigade == NULL) { + return NULL; + } + return ctx; +} + +static void +ngx_http_modsecurity_cleanup(void *data) +{ + ngx_http_modsecurity_ctx_t *ctx = data; + + if (ctx->req != NULL) { + (void) modsecFinishRequest(ctx->req); + } +} + +static char * +ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_modsecurity_loc_conf_t *mscf = conf; + ngx_str_t *value; + const char *msg; + + if (mscf->config != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + 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_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) +{ + 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 -1; + } + ctx->r->connection->error = 1; + return 0; +} + diff --git a/standalone/api.c b/standalone/api.c index 04bbc4c858..b5fea42eb3 100644 --- a/standalone/api.c +++ b/standalone/api.c @@ -180,7 +180,12 @@ apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, bb_in = modsecGetBodyBrigade(f->r); /* use request brigade */ - if (bb_in) { + 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); + } + rv = apr_brigade_partition(bb_in, readbytes, &after); if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { return rv; @@ -190,48 +195,62 @@ apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, APR_BUCKET_REMOVE(e); APR_BRIGADE_INSERT_TAIL(bb_out, e); } - + return APR_SUCCESS; } /* call the callback */ - if(modsecReadBody == NULL) - return AP_NOBODY_READ; - - tmp = (char *)apr_palloc(f->r->pool, readbytes); - modsecReadBody(f->r, tmp, readbytes, &readcnt, &is_eos); + if(modsecReadBody != NULL) { - e = apr_bucket_pool_create(tmp, readcnt, f->r->pool, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb_out, e); + tmp = (char *)apr_palloc(f->r->pool, readbytes); + modsecReadBody(f->r, tmp, readbytes, &readcnt, &is_eos); - if(is_eos) { - e = apr_bucket_eos_create(f->c->bucket_alloc); + 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 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; @@ -483,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, @@ -500,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; From b536e8b43f0fbac214c1fa3da97ec38316176cc9 Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Thu, 17 Jan 2013 16:33:46 +0800 Subject: [PATCH 09/11] Removed unused member. --- nginx/modsecurity/ngx_http_modsecurity.c | 1 - 1 file changed, 1 deletion(-) diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 1dee180ed7..574d839f26 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -44,7 +44,6 @@ typedef struct { apr_bucket_brigade *brigade; unsigned complete; - unsigned blocked; } ngx_http_modsecurity_ctx_t; From 53b63973e31bbad36ee431a083a70551e4dd8dca Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Fri, 18 Jan 2013 22:14:05 +0800 Subject: [PATCH 10/11] Fixed log format error --- nginx/modsecurity/ngx_http_modsecurity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 574d839f26..1dabf8a46c 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -185,7 +185,7 @@ ngx_http_modsecurity_merge_loc_conf(ngx_conf_t *cf, void *parent, 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); + conf->file, conf->line); return NGX_CONF_ERROR; } From c15f1bb9b681c0bd65c469700a6740b2324564bf Mon Sep 17 00:00:00 2001 From: chaizhenhua Date: Sat, 19 Jan 2013 15:43:08 +0800 Subject: [PATCH 11/11] Fixed multipart error --- nginx/modsecurity/apr_bucket_nginx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx/modsecurity/apr_bucket_nginx.c b/nginx/modsecurity/apr_bucket_nginx.c index 3bafb30384..d9b0155486 100644 --- a/nginx/modsecurity/apr_bucket_nginx.c +++ b/nginx/modsecurity/apr_bucket_nginx.c @@ -82,7 +82,7 @@ static void nginx_bucket_destroy(void *data) apr_bucket_nginx *n = data; ngx_buf_t *buf = n->buf; - if (apr_bucket_shared_destroy(buf)) { + if (apr_bucket_shared_destroy(n)) { if (!ngx_buf_in_memory(buf) && buf->pos != NULL) { apr_bucket_free(buf->pos); buf->pos = NULL;