diff --git a/nginx/modsecurity/apr_bucket_nginx.c b/nginx/modsecurity/apr_bucket_nginx.c index d9b0155486..7b41b6f0ee 100644 --- a/nginx/modsecurity/apr_bucket_nginx.c +++ b/nginx/modsecurity/apr_bucket_nginx.c @@ -2,7 +2,7 @@ #include static apr_status_t nginx_bucket_read(apr_bucket *b, const char **str, - apr_size_t *len, apr_read_type_e block); + 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 = { @@ -110,14 +110,14 @@ ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool) { 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)) { + + if (ngx_buf_in_memory(buf)) { buf->start = buf->pos = buf->pos + e->start; buf->end = buf->last = buf->pos + e->length; } else { @@ -125,7 +125,7 @@ ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool) { buf->file_pos += e->start; buf->file_last = buf->file_pos + e->length; } - + buf->last_buf = 0; return buf; } @@ -134,7 +134,7 @@ ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool) { &len, APR_BLOCK_READ) != APR_SUCCESS) { return NULL; } - + buf = ngx_calloc_buf(pool); if (buf == NULL) { return NULL; @@ -146,7 +146,7 @@ ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool) { 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; @@ -154,10 +154,10 @@ ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool) { } ngx_int_t -move_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *pool) { +move_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *pool, ngx_int_t last_buf) { apr_bucket *e; ngx_chain_t *cl; - + while (chain) { e = ngx_buf_to_apr_bucket(chain->buf, bb->p, bb->bucket_alloc); if (e == NULL) { @@ -175,6 +175,13 @@ move_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *po chain = chain->next; ngx_free_chain(pool, cl); } + + if (last_buf) { + e = apr_bucket_eos_create(bb->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + return NGX_OK; + } + return NGX_AGAIN; } @@ -185,16 +192,16 @@ move_brigade_to_chain(apr_bucket_brigade *bb, ngx_chain_t **ll, ngx_pool_t *pool ngx_chain_t *cl; cl = NULL; - + if (APR_BRIGADE_EMPTY(bb)) { *ll = NULL; return NGX_OK; } - + for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) { - + if (APR_BUCKET_IS_EOS(e)) { if (cl == NULL) { *ll = cl; @@ -204,7 +211,7 @@ move_brigade_to_chain(apr_bucket_brigade *bb, ngx_chain_t **ll, ngx_pool_t *pool apr_brigade_cleanup(bb); return NGX_OK; } - + if (APR_BUCKET_IS_METADATA(e)) { continue; } @@ -213,12 +220,12 @@ move_brigade_to_chain(apr_bucket_brigade *bb, ngx_chain_t **ll, ngx_pool_t *pool if (buf == NULL) { break; } - + cl = ngx_alloc_chain_link(pool); if (cl == NULL) { break; } - + cl->buf = buf; cl->next = NULL; *ll = cl; diff --git a/nginx/modsecurity/apr_bucket_nginx.h b/nginx/modsecurity/apr_bucket_nginx.h index 4dd1ca0ff1..e37f9f78c8 100644 --- a/nginx/modsecurity/apr_bucket_nginx.h +++ b/nginx/modsecurity/apr_bucket_nginx.h @@ -13,6 +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); -ngx_int_t move_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, 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 last_buf); 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 8140002ed2..58e61efcdd 100644 --- a/nginx/modsecurity/config +++ b/nginx/modsecurity/config @@ -1,9 +1,8 @@ ngx_addon_name=ngx_http_modsecurity -# HTTP_MODULES="$HTTP_MODULES ngx_http_modsecurity" -HTTP_HEADERS_FILTER_MODULE="ngx_http_modsecurity $HTTP_HEADERS_FILTER_MODULE" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_modsecurity.c $ngx_addon_dir/apr_bucket_nginx.c" -NGX_ADDON_DEPS="$NGX_ADDON_DEPS" +CORE_MODULES="$CORE_MODULES ngx_pool_context_module" +HTTP_AUX_FILTER_MODULES="ngx_http_modsecurity $HTTP_AUX_FILTER_MODULES" +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_modsecurity.c $ngx_addon_dir/apr_bucket_nginx.c $ngx_addon_dir/ngx_pool_context.c" +NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/apr_bucket_nginx.h $ngx_addon_dir/ngx_pool_context.h" 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 /usr/include/httpd /usr/include/apr-1 $ngx_addon_dir $ngx_addon_dir/../../standalone $ngx_addon_dir/../../apache2 /usr/include/libxml2" -have=REQUEST_EARLY . auto/have diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 2ed89d1760..6eeeef05e0 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -12,15 +12,10 @@ * directly using the email address security@modsecurity.org. */ -#include -#include -#include #include -#include -#include -#include -#include #include +#include + #undef CR #undef LF #undef CRLF @@ -32,7 +27,7 @@ typedef struct { ngx_flag_t enable; directory_config *config; - + ngx_str_t *file; ngx_uint_t line; } ngx_http_modsecurity_loc_conf_t; @@ -66,7 +61,10 @@ static char *ngx_http_modsecurity_enable(ngx_conf_t *cf, ngx_command_t *cmd, voi 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); + +static int ngx_http_modsecurity_save_headers_in_visitor(void *data, const char *key, const char *value); +static int ngx_http_modsecurity_save_headers_out_visitor(void *data, const char *key, const char *value); + /* command handled by the module */ static ngx_command_t ngx_http_modsecurity_commands[] = { @@ -120,14 +118,160 @@ 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; +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_http_upstream_t ngx_http_modsecurity_upstream; + +static struct { + char *name; + ngx_str_t variable_name; +} special_headers_out[] = { + {"Content-Type", ngx_string("sent_http_content_type") }, + {"Content-Length", ngx_string("sent_http_content_length")}, + {"Location", ngx_string("sent_http_location")}, + {"Last-Modified", ngx_string("sent_http_last_modified")}, + {"Connection", ngx_string("sent_http_connection")}, + {"Keep-Alive", ngx_string("sent_http_keep_alive")}, + {"Transfer-Encoding", ngx_string("sent_http_transfer_encoding")}, + {"Cache-Control", ngx_string("sent_http_cache_control")}, + {NULL, ngx_null_string} +}; + + +static inline int ngx_http_modsecurity_method_number(unsigned int nginx) +{ + /* + * http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup + */ + static const int MultiplyDeBruijnBitPosition[32] = { + M_INVALID, /* 1 >> 0 */ + M_GET, + M_INVALID, /* 1 >> 28 */ + M_GET, /* NGX_HTTP_HEAD */ + M_INVALID, /* 1 >> 29 */ + M_PATCH, + M_INVALID, /* 1 >> 24 */ + M_POST, + M_INVALID, /* 1 >> 30 */ + M_INVALID, /* 1 >> 22 */ + M_INVALID, /* 1 >> 20 */ + M_TRACE, + M_INVALID, /* 1 >> 25 */ + M_INVALID, /* 1 >> 17 */ + M_PUT, + M_MOVE, + M_INVALID, /* 1 >> 31 */ + M_INVALID, /* 1 >> 27 */ + M_UNLOCK, + M_INVALID, /* 1 >> 23 */ + M_INVALID, /* 1 >> 21 */ + M_INVALID, /* 1 >> 19 */ + M_INVALID, /* 1 >> 16 */ + M_COPY, + M_INVALID, /* 1 >> 26 */ + M_LOCK, + M_INVALID, /* 1 >> 18 */ + M_MKCOL, + M_PROPPATCH, + M_DELETE, + M_PROPFIND, + M_OPTIONS + }; + + return MultiplyDeBruijnBitPosition[((uint32_t)((nginx & -nginx) * 0x077CB531U)) >> 27]; +} + +static ngx_inline ngx_int_t +ngx_http_modsecurity_load_request(ngx_http_request_t *r) +{ + ngx_http_modsecurity_ctx_t *ctx; + request_rec *req; + ngx_str_t str; + size_t root; + ngx_str_t path; + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + req = ctx->req; + + /* request line */ + req->method = (char *)ngx_pstrdup(r->pool, &r->method_name); + + /* TODO: how to use ap_method_number_of ? + * req->method_number = ap_method_number_of(req->method); + */ + + req->method_number = ngx_http_modsecurity_method_number(r->method); + + /* ngx_http_map_uri_to_path() allocates memory for terminating '\0' */ + if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) { + return NGX_ERROR; + } + + req->filename = (char *) path.data; + req->path_info = req->filename; + + req->args = (char *)ngx_pstrdup(r->pool, &r->args); + + req->proto_num = r->http_major *1000 + r->http_minor; + req->protocol = (char *)ngx_pstrdup(r->pool, &r->http_protocol); + req->request_time = apr_time_make(r->start_sec, r->start_msec); + req->the_request = (char *)ngx_pstrdup(r->pool, &r->request_line); + + req->unparsed_uri = (char *)ngx_pstrdup(r->pool, &r->unparsed_uri); + req->uri = (char *)ngx_pstrdup(r->pool, &r->uri); + + req->parsed_uri.scheme = "http"; + +#if (NGX_HTTP_SSL) + if (r->connection->ssl) { + req->parsed_uri.scheme = "https"; + } +#endif + + req->parsed_uri.path = req->path_info; + req->parsed_uri.is_initialized = 1; + + str.data = r->port_start; + str.len = r->port_end - r->port_start; + req->parsed_uri.port = ngx_atoi(str.data, str.len); + req->parsed_uri.port_str = (char *)ngx_pstrdup(r->pool, &str); + + req->parsed_uri.query = req->args; + req->parsed_uri.dns_looked_up = 0; + req->parsed_uri.dns_resolved = 0; + + // req->parsed_uri.password = (char *)ngx_pstrdup(r->pool, &r->headers_in.passwd); + // req->parsed_uri.user = (char *)ngx_pstrdup(r->pool, &r->headers_in.user); + req->parsed_uri.fragment = (char *)ngx_pstrdup(r->pool, &r->exten); + + req->hostname = (char *)ngx_pstrdup(r->pool, (ngx_str_t *)&ngx_cycle->hostname); + + req->header_only = r->header_only ? r->header_only : (r->method == NGX_HTTP_HEAD); + + return NGX_OK; +} + + +/* + * TODO: deal more headers. + */ + +static ngx_inline ngx_int_t +ngx_http_modsecurity_load_headers_in(ngx_http_request_t *r) +{ + ngx_http_modsecurity_ctx_t *ctx; + const char *lang; + request_rec *req; + ngx_list_part_t *part; + ngx_table_elt_t *h; + ngx_uint_t i; + + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + req = ctx->req; + + part = &r->headers_in.headers.part; h = part->elts; for (i = 0; ; i++) { @@ -140,19 +284,489 @@ ngx_list_copy_to_apr_table(ngx_list_t *list, apr_table_t *table, apr_pool_t *poo i = 0; } - key = ConvertNgxStringToUTF8(h[i].key, pool); - if (key == NULL) { + apr_table_setn(req->headers_in, (char *)h[i].key.data, (char *)h[i].value.data); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: load headers in: \"%V: %V\"", + &h[i].key, &h[i].value); + } + + req->clength = r->headers_in.content_length_n; + + + req->range = apr_table_get(req->headers_in, "Range"); + req->content_type = apr_table_get(req->headers_in, "Content-Type"); + req->content_encoding = apr_table_get(req->headers_in, "Content-Encoding"); + + 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; + } + + req->ap_auth_type = (char *)apr_table_get(req->headers_in, "Authorization"); + + req->user = (char *)ngx_pstrdup(r->pool, &r->headers_in.user); + + + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: load headers in done"); + + return NGX_OK; +} + +static ngx_inline ngx_int_t +ngx_http_modsecurity_save_headers_in(ngx_http_request_t *r) +{ + ngx_http_modsecurity_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + + /* clean up headers_in */ + ngx_memzero(&r->headers_in, sizeof(ngx_http_headers_in_t)); + + if (ngx_list_init(&r->headers_in.headers, r->pool, 20, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + + if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, + sizeof(ngx_table_elt_t *)) + != NGX_OK) + { + return NGX_ERROR; + } + + r->headers_in.content_length_n = -1; + r->headers_in.keep_alive_n = -1; + + r->headers_in.headers.part.nelts = 0; + r->headers_in.headers.part.next = NULL; + r->headers_in.headers.last = &r->headers_in.headers.part; + + /* shadow copy */ + if (apr_table_do(ngx_http_modsecurity_save_headers_in_visitor, + r, ctx->req->headers_in, NULL) == 0) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: save headers in error"); + + return NGX_ERROR; + } + + if (r->headers_in.content_length) { + r->headers_in.content_length_n = + ngx_atoof(r->headers_in.content_length->value.data, + r->headers_in.content_length->value.len); + + if (r->headers_in.content_length_n == NGX_ERROR) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "ModSecurity: invalid \"Content-Length\" header"); return NGX_ERROR; } - - value = ConvertNgxStringToUTF8(h[i].value, pool); - if (value == NULL) { + } + + if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) { + if (r->headers_in.keep_alive) { + r->headers_in.keep_alive_n = + ngx_atotm(r->headers_in.keep_alive->value.data, + r->headers_in.keep_alive->value.len); + } + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: save headers in done"); + + return NGX_OK; +} + + +static int +ngx_http_modsecurity_save_headers_in_visitor(void *data, const char *key, const char *value) +{ + ngx_http_request_t *r = data; + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + return 0; + } + + h->key.data = (u_char *)key; + h->key.len = ngx_strlen(key); + + h->value.data = (u_char *)value; + h->value.len = ngx_strlen(value); + + h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); + + if (h->lowcase_key == NULL) { + return 0; + } + + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); + + h->hash = ngx_hash_key(h->lowcase_key, h->key.len); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { + return 0; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: save headers in: \"%V: %V\"", + &h->key, &h->value); + + return 1; +} + + +static ngx_inline ngx_int_t +ngx_http_modsecurity_load_request_body(ngx_http_request_t *r) +{ + ngx_http_modsecurity_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + + modsecSetBodyBrigade(ctx->req, ctx->brigade); + + if (r->request_body == NULL || r->request_body->bufs == NULL) { + + return move_chain_to_brigade(NULL, ctx->brigade, r->pool, 1); + } + + if (move_chain_to_brigade(r->request_body->bufs, ctx->brigade, r->pool, 1) != NGX_OK) { + return NGX_ERROR; + } + + r->request_body = NULL; + + return NGX_OK; +} + + +static ngx_inline ngx_int_t +ngx_http_modsecurity_save_request_body(ngx_http_request_t *r) +{ + ngx_http_modsecurity_ctx_t *ctx; + apr_off_t content_length; + ngx_buf_t *buf; + ngx_http_core_srv_conf_t *cscf; + size_t size; + ngx_http_connection_t *hc; + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + + apr_brigade_length(ctx->brigade, 0, &content_length); + + if (r->header_in->end - r->header_in->last >= content_length) { + /* use r->header_in */ + + if (ngx_buf_size(r->header_in)) { + /* move to the end */ + ngx_memmove(r->header_in->pos + content_length, + r->header_in->pos, + ngx_buf_size(r->header_in)); + } + + if (apr_brigade_flatten(ctx->brigade, + (char *)r->header_in->pos, + (apr_size_t *)&content_length) != APR_SUCCESS) { + return NGX_ERROR; + } + + apr_brigade_cleanup(ctx->brigade); + + r->header_in->last += content_length; + + return NGX_OK; + } + + if (ngx_buf_size(r->header_in)) { + + /* + * ngx_http_set_keepalive will reuse r->header_in if + * (r->header_in != c->buffer && r->header_in.last != r->header_in.end), + * so we need this code block. + * see ngx_http_set_keepalive, ngx_http_alloc_large_header_buffer + */ + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + size = ngx_max(cscf->large_client_header_buffers.size, + (size_t)content_length + ngx_buf_size(r->header_in)); + + hc = r->http_connection; + + if (hc->nfree && size == cscf->large_client_header_buffers.size) { + + buf = hc->free[--hc->nfree]; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: use http free large header buffer: %p %uz", + buf->pos, buf->end - buf->last); + + } else if (hc->nbusy < cscf->large_client_header_buffers.num) { + + if (hc->busy == NULL) { + hc->busy = ngx_palloc(r->connection->pool, + cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *)); + } + + if (hc->busy == NULL) { + return NGX_ERROR; + } else { + buf = ngx_create_temp_buf(r->connection->pool, size); + } + } else { + /* TODO: how to deal this case ? */ + return NGX_ERROR; + } + + } else { + + buf = ngx_create_temp_buf(r->pool, (size_t) content_length); + } + + if (buf == NULL) { + return NGX_ERROR; + } + + if (apr_brigade_flatten(ctx->brigade, (char *)buf->pos, + (apr_size_t *)&content_length) != APR_SUCCESS) { + return NGX_ERROR; + } + + apr_brigade_cleanup(ctx->brigade); + buf->last += content_length; + + ngx_memcpy(buf->last, r->header_in->pos, ngx_buf_size(r->header_in)); + buf->last += ngx_buf_size(r->header_in); + + r->header_in = buf; + + return NGX_OK; +} + + +static ngx_inline ngx_int_t +ngx_http_modsecurity_load_headers_out(ngx_http_request_t *r) +{ + + ngx_http_modsecurity_ctx_t *ctx; + char *data; + request_rec *req; + u_char *content_type; + ngx_uint_t content_type_len; + ngx_http_variable_value_t *vv; + ngx_list_part_t *part; + ngx_table_elt_t *h; + ngx_uint_t i; + char *key, *value; + u_char *buf = NULL; + size_t size = 0; + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + req = ctx->req; + + req->status = r->headers_out.status; + req->status_line = (char *)ngx_pstrdup(r->pool, &r->headers_out.status_line); + + if (r->headers_out.charset.len) { + + content_type_len = r->headers_out.content_type.len + + r->headers_out.charset.len + + ngx_strlen("; charset=") + 1; + + content_type = ngx_palloc(r->pool, content_type_len); + + if (content_type == NULL) { + return NGX_ERROR; + } + + ngx_snprintf(content_type, content_type_len, + "%V; charset=%V", + &r->headers_out.content_type, + &r->headers_out.charset); + + r->headers_out.content_type.data = content_type; + r->headers_out.content_type.len = content_type_len; + } + + /* deep copy */ + part = &r->headers_out.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; + } + size += h[i].key.len + h[i].value.len + 2; + + buf = ngx_palloc(r->pool, size); + + if (buf == NULL) { return NGX_ERROR; } - apr_table_setn(table, key, value); + key = (char *)buf; + buf = ngx_cpymem(buf, h[i].key.data, h[i].key.len); + *buf++ = '\0'; + + value = (char *)buf; + buf = ngx_cpymem(buf, h[i].value.data, h[i].value.len); + *buf++ = '\0'; + + apr_table_setn(req->headers_out, key, value); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: load headers out: \"%V: %V\"", + &h[i].key, &h[i].value); + + } + + for (i = 0; special_headers_out[i].name; i++) { + + vv = ngx_http_get_variable(r, &special_headers_out[i].variable_name, + ngx_hash_key(special_headers_out[i].variable_name.data, + special_headers_out[i].variable_name.len)); + + if (vv && !vv->not_found) { + + data = ngx_palloc(r->pool, vv->len + 1); + if (data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(data,vv->data, vv->len); + data[vv->len] = '\0'; + + apr_table_setn(req->headers_out, special_headers_out[i].name, data); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: load headers out: \"%s: %s\"", + special_headers_out[i].name, data); + } + } + + req->content_type = apr_table_get(ctx->req->headers_out, "Content-Type"); + req->content_encoding = apr_table_get(ctx->req->headers_out, "Content-Encoding"); + + data = (char *)apr_table_get(ctx->req->headers_out, "Content-Languages"); + + if(data != NULL) + { + ctx->req->content_languages = apr_array_make(ctx->req->pool, 1, sizeof(const char *)); + *(const char **)apr_array_push(ctx->req->content_languages) = data; + } + + /* req->chunked = r->chunked; may be useless */ + req->clength = r->headers_out.content_length_n; + req->mtime = apr_time_make(r->headers_out.last_modified_time, 0); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: load headers out done"); + + return NGX_OK; +} + + +static ngx_inline ngx_int_t +ngx_http_modsecurity_save_headers_out(ngx_http_request_t *r) +{ + ngx_http_modsecurity_ctx_t *ctx; + ngx_http_upstream_t *upstream; + + ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); + + /* r->chunked = ctx->req->chunked; */ + + ngx_http_clean_header(r); + + upstream = r->upstream; + r->upstream = &ngx_http_modsecurity_upstream; + + if (apr_table_do(ngx_http_modsecurity_save_headers_out_visitor, + r, ctx->req->headers_out, NULL) == 0) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: save headers out error"); + + return NGX_ERROR; + } + + r->upstream = upstream; + + r->headers_out.status = ctx->req->status; + r->headers_out.status_line.data = (u_char *)ctx->req->status_line; + r->headers_out.status_line.len = ctx->req->status_line ? + ngx_strlen(ctx->req->status_line) : 0; + + r->headers_out.content_length_n = ctx->req->clength; + r->headers_out.last_modified_time = apr_time_sec(ctx->req->mtime); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: save headers out done"); + + return NGX_OK; +} + + +static int +ngx_http_modsecurity_save_headers_out_visitor(void *data, const char *key, const char *value) +{ + ngx_http_request_t *r = data; + ngx_table_elt_t *h, he; + ngx_http_upstream_header_t *hh; + ngx_http_upstream_main_conf_t *umcf; + + umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); + + h = &he; + + h->key.data = (u_char *)key; + h->key.len = ngx_strlen(key); + + h->value.data = (u_char *)value; + h->value.len = ngx_strlen(value); + + h->lowcase_key = ngx_palloc(r->pool, h->key.len); + if (h->lowcase_key == NULL) { + return 0; } - return NGX_OK; + + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); + + h->hash = ngx_hash_key(h->lowcase_key, h->key.len); + + hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh) { + /* copy all */ + if (hh->copy_handler(r, h, hh->conf) != NGX_OK) { + return 0; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ModSecurity: save headers out: \"%V: %V\"", + &h->key, &h->value); + + return 1; } /* create loc conf struct */ @@ -161,7 +775,8 @@ 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)); + conf = (ngx_http_modsecurity_loc_conf_t *) + ngx_palloc(cf->pool, sizeof(ngx_http_modsecurity_loc_conf_t)); if (conf == NULL) return NULL; @@ -173,8 +788,8 @@ ngx_http_modsecurity_create_loc_conf(ngx_conf_t *cf) /* merge loc conf */ static char * -ngx_http_modsecurity_merge_loc_conf(ngx_conf_t *cf, void *parent, - void *child) +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; @@ -184,10 +799,10 @@ ngx_http_modsecurity_merge_loc_conf(ngx_conf_t *cf, void *parent, if (conf->enable && conf->config == NULL) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "\"ModSecurityEnabled\" in %V:%ui is set to \"on\"" - " while directive \"ModSecurityConfig\" is not found" - " in the same location", - conf->file, conf->line); + "\"ModSecurityEnabled\" in %V:%ui is set to \"on\"" + " while directive \"ModSecurityConfig\" is not found" + " in the same location", + conf->file, conf->line); return NGX_CONF_ERROR; } @@ -235,6 +850,7 @@ ngx_http_modsecurity_preconfiguration(ngx_conf_t *cf) modsecSetLogHook(cf->log, modsecLog); modsecSetDropAction(ngx_http_modsecurity_drop_action); + /* TODO: server_rec per server conf */ s = modsecInit(); if (s == NULL) { return NGX_ERROR; @@ -252,10 +868,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 + +static ngx_int_t ngx_http_modsecurity_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; @@ -263,7 +878,7 @@ ngx_http_modsecurity_init(ngx_conf_t *cf) modsecFinalizeConfig(); - cmcf = (ngx_http_core_main_conf_t *) ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); if (cmcf == NULL) { return NGX_ERROR; } @@ -279,6 +894,9 @@ ngx_http_modsecurity_init(ngx_conf_t *cf) ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_modsecurity_body_filter; + + ngx_memzero(&ngx_http_modsecurity_upstream, sizeof(ngx_http_upstream_t)); + ngx_http_modsecurity_upstream.cacheable = 1; return NGX_OK; } @@ -298,56 +916,6 @@ ngx_http_modsecurity_exit_process(ngx_cycle_t *cycle) } -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 */ @@ -361,7 +929,20 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); /* Process only main request */ - if (r != r->main || r->internal || !cf->enable) { + if (r != r->main || !cf->enable) { + return NGX_DECLINED; + } + + if (r->internal) { + + ctx = ngx_http_get_module_pool_ctx(r, ngx_http_modsecurity); + + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); + return NGX_DECLINED; } @@ -369,15 +950,32 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) ctx = ngx_http_modsecurity_create_ctx(r); if (ctx == NULL) { - return NGX_ERROR; + + return NGX_HTTP_INTERNAL_SERVER_ERROR; } + ngx_http_set_ctx(r, ctx, ngx_http_modsecurity); + if (ngx_http_set_pool_ctx(r, ctx, ngx_http_modsecurity) != NGX_OK) { + return NGX_ERROR; + } + + ngx_http_modsecurity_load_request(r); + + if (ngx_http_modsecurity_load_headers_in(r) != NGX_OK) { + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + /* processing request headers */ rc = modsecProcessRequestHeaders(ctx->req); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: modsecProcessRequestHeaders %d", rc); + if (rc == DECLINED) { - if (r->method == NGX_HTTP_POST) { + + if (modsecIsRequestBodyAccessEnabled(ctx->req) + && r->method == NGX_HTTP_POST) { + /* Processing POST request body, should we process PUT? */ rc = ngx_http_read_client_request_body(r, ngx_http_modsecurity_body_handler); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { @@ -387,10 +985,8 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) return NGX_DONE; } /* other method */ - if(modsecIsRequestBodyAccessEnabled(ctx->req)) { - rc = modsecProcessRequestBody(ctx->req); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: modsecProcessRequestBody %d", rc); - } + rc = modsecProcessRequestBody(ctx->req); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: modsecProcessRequestBody %d", rc); } if (rc != DECLINED) { @@ -402,6 +998,13 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) return rc; } + /* + if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK) { + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + */ + return NGX_DECLINED; } @@ -409,38 +1012,21 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) static void ngx_http_modsecurity_body_handler(ngx_http_request_t *r) { - ngx_http_modsecurity_ctx_t *ctx = NULL; - ngx_int_t rc = DECLINED; - apr_off_t content_length; - ngx_str_t *str = NULL; - ngx_buf_t *buf = NULL; - - 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_http_modsecurity_ctx_t *ctx; + ngx_int_t rc; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: body handler"); 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); - } + if (ngx_http_modsecurity_load_request_body(r) != NGX_OK) { - rc = DECLINED; - r->request_body = NULL; + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } - modsecSetBodyBrigade(ctx->req, ctx->brigade); + rc = modsecProcessRequestBody(ctx->req); - if(modsecIsRequestBodyAccessEnabled(ctx->req)) { - rc = modsecProcessRequestBody(ctx->req); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: modsecProcessRequestBody %d", rc); - } if (rc != DECLINED) { /* Nginx and Apache share same response code */ @@ -450,29 +1036,12 @@ ngx_http_modsecurity_body_handler(ngx_http_request_t *r) 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); - } + if (ngx_http_modsecurity_save_request_body(r) != NGX_OK + || ngx_http_modsecurity_save_headers_in(r) != NGX_OK ) { - 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; + return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } - r->headers_in.content_length_n = content_length; r->phase_handler++; ngx_http_core_run_phases(r); ngx_http_finalize_request(r, NGX_DONE); @@ -483,55 +1052,53 @@ 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; ngx_int_t rc; - + 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) { + + if (r != r->main || !cf->enable || ctx->complete) { return ngx_http_next_header_filter(r); } - + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: header filter"); - - /* 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; - } + if (r->method == NGX_HTTP_HEAD || r->header_only) { - 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"); + ctx->complete = 1; - 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; - } + // TODO: do we need reload headers_in ? + + if (ngx_http_modsecurity_load_headers_in(r) != NGX_OK + || ngx_http_modsecurity_load_headers_out(r) != NGX_OK) { + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } - if (r->method == NGX_HTTP_HEAD || r->header_only) { - - ctx->complete = 1; rc = modsecProcessResponse(ctx->req); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: modsecProcessResponse %d", rc); + if (rc == DECLINED || rc == APR_SUCCESS) { + + if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK + || ngx_http_modsecurity_save_headers_out(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + return ngx_http_next_header_filter(r); } if (rc < NGX_HTTP_SPECIAL_RESPONSE || rc >= 600) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } - + return rc; - } + } return NGX_OK; } + static ngx_int_t ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { @@ -543,7 +1110,7 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) 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) { + if (r != r->main || !cf->enable || ctx->complete) { return ngx_http_next_body_filter(r, in); } @@ -553,7 +1120,7 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) return NGX_AGAIN; } - rc = move_chain_to_brigade(in, ctx->brigade, r->pool); + rc = move_chain_to_brigade(in, ctx->brigade, r->pool, 0); if (rc != NGX_OK) { return rc; } @@ -563,6 +1130,14 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ctx->complete = 1; modsecSetResponseBrigade(ctx->req, ctx->brigade); + // TODO: do we need reload headers_in ? + // + if (ngx_http_modsecurity_load_headers_in(r) != NGX_OK + || ngx_http_modsecurity_load_headers_out(r) != NGX_OK) { + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + rc = modsecProcessResponse(ctx->req); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: modsecProcessResponse %d", rc); @@ -571,18 +1146,24 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) in = NULL; apr_brigade_length(ctx->brigade, 0, &content_length); - - rc = move_brigade_to_chain(ctx->brigade, &in, ctx->r->pool); + + rc = move_brigade_to_chain(ctx->brigade, &in, r->pool); if (rc == NGX_ERROR) { return NGX_ERROR; } + if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK + ||ngx_http_modsecurity_save_headers_out(r) != NGX_OK) { + + return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_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) { @@ -590,7 +1171,7 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) } return ngx_http_next_body_filter(r, in); - } + } if (rc < NGX_HTTP_SPECIAL_RESPONSE || rc >= 600) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -606,7 +1187,12 @@ 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; + apr_sockaddr_t *asa; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_modsecurity_ctx_t)); if (ctx == NULL) { @@ -620,73 +1206,70 @@ ngx_http_modsecurity_create_ctx(ngx_http_request_t *r) } cln->handler = ngx_http_modsecurity_cleanup; cln->data = ctx; - + ctx->r = r; - + if (r->connection->requests == 0 || ctx->connection == NULL) { + + /* TODO: set server_rec, why igonre return value? */ ctx->connection = modsecNewConnection(); + + /* fill apr_sockaddr_t */ + asa = ngx_palloc(r->pool, sizeof(apr_sockaddr_t)); + asa->pool = ctx->connection->pool; + asa->hostname = (char *)ngx_pstrdup(r->pool, &r->connection->addr_text); + asa->servname = asa->hostname; + asa->next = NULL; + asa->salen = r->connection->socklen; + ngx_memcpy(&asa->sa, r->connection->sockaddr, asa->salen); + + asa->family = ((struct sockaddr *)&asa->sa)->sa_family; + switch ( asa->family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *)&asa->sa; + asa->ipaddr_ptr = &sin6->sin6_addr; + asa->ipaddr_len = sizeof(sin6->sin6_addr); + asa->port = ntohs(sin6->sin6_port); + asa->addr_str_len = NGX_INET6_ADDRSTRLEN + 1; + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) &asa->sa; + asa->ipaddr_ptr = &sin->sin_addr; + asa->ipaddr_len = sizeof(sin->sin_addr); + asa->port = ntohs(sin->sin_port); + asa->addr_str_len = NGX_INET_ADDRSTRLEN + 1; + break; + } + + #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); + ctx->connection->remote_addr = asa; + ctx->connection->remote_ip = asa->hostname; #else - ctx->connection->client_addr = CopySockAddr(ctx->connection->pool, r->connection->sockaddr); - ctx->connection->client_ip = ConvertNgxStringToUTF8(r->connection->addr_text, ctx->connection->pool); + ctx->connection->client_addr = asa; + ctx->connection->client_ip = asa->hostname; #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->notes, NOTE_NGINX_REQUEST_CTX, (const char *) ctx); 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; } - apr_table_setn(ctx->req->notes, NOTE_NGINX_REQUEST_CTX, (const char *) ctx); - return ctx; } @@ -694,7 +1277,7 @@ static void ngx_http_modsecurity_cleanup(void *data) { ngx_http_modsecurity_ctx_t *ctx = data; - + if (ctx->req != NULL) { (void) modsecFinishRequest(ctx->req); } @@ -725,19 +1308,21 @@ ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) msg = modsecProcessConfig(mscf->config, (const char *)value[1].data, NULL); if (msg != NULL) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "ModSecurityConfig in %s:%ui: %s", + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "ModSecurityConfig in %s:%ui: %s", cf->conf_file->file.name.data, cf->conf_file->line, 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; diff --git a/nginx/modsecurity/ngx_pool_context.c b/nginx/modsecurity/ngx_pool_context.c new file mode 100644 index 0000000000..988c893330 --- /dev/null +++ b/nginx/modsecurity/ngx_pool_context.c @@ -0,0 +1,212 @@ + + +#include + +#define NGX_POOL_CTX_SIZE 1024 + +typedef struct ngx_pool_context_node_s ngx_pool_context_node_t; +struct ngx_pool_context_node_s +{ + ngx_pool_context_node_t *next; + ngx_pool_context_node_t **prev; + ngx_pool_t *pool; + ngx_uint_t index; + void *data; +}; + +static void +ngx_pool_context_cleanup(void *data); + +typedef struct { + ngx_uint_t size; +} ngx_pool_context_conf_t; + +static void * ngx_pool_context_create_conf(ngx_cycle_t *cycle); +static char * ngx_pool_context_init_conf(ngx_cycle_t *cycle, void *conf); + +static ngx_core_module_t ngx_pool_context_module_ctx = { + ngx_string("pool_context"), + ngx_pool_context_create_conf, + ngx_pool_context_init_conf, +}; + +static ngx_command_t ngx_pool_context_commands[] = { + + { ngx_string("pool_context_hash_size"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_pool_context_conf_t, size), + NULL + }, + ngx_null_command +}; + + +ngx_module_t ngx_pool_context_module = { + NGX_MODULE_V1, + &ngx_pool_context_module_ctx, /* module context */ + ngx_pool_context_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +#define ngx_pool_context_hash_key(r, ctx_index) ((ngx_uint_t) r + ctx_index) + +#define ngx_pool_context_unlink(node) \ + \ + *(node->prev) = node->next; \ + \ + if (node->next) { \ + node->next->prev = node->prev; \ + } \ + \ + node->prev = NULL; \ + + +#define ngx_pool_context_link(queue, node) \ + \ + if (node->prev != NULL) { \ + ngx_pool_context_unlink(node); \ + } \ + node->next = (ngx_pool_context_node_t *) *queue; \ + node->prev = (ngx_pool_context_node_t **) queue; \ + *queue = node; \ + \ + if (node->next) { \ + node->next->prev = &node->next; \ + } + + +static ngx_pool_context_node_t **ngx_pool_context_hash; +static ngx_uint_t ngx_pool_context_hash_size; + +/* Nginx has removed multi-thread support, so we do not need mutex */ + +void * +ngx_pool_get_ctx(ngx_pool_t *pool, ngx_uint_t index) +{ + ngx_uint_t hash; + uint32_t key; + ngx_pool_context_node_t *node; + + hash = (ngx_uint_t) pool + index; + key = ngx_murmur_hash2((u_char *)&hash, sizeof(hash)) % ngx_pool_context_hash_size; + + node = ngx_pool_context_hash[key]; + + while (node) { + + if (node->pool == pool && node->index == index) { + + return node->data; + } + node = node->next; + } + + return NULL; + +} + + +ngx_int_t +ngx_pool_set_ctx(ngx_pool_t *pool, ngx_uint_t index, void *data) +{ + ngx_uint_t hash; + uint32_t key; + ngx_pool_context_node_t *node; + ngx_pool_cleanup_t *cln; + + hash = (ngx_uint_t) pool + index; + key = ngx_murmur_hash2((u_char *)&hash, sizeof(hash)) % ngx_pool_context_hash_size; + + node = ngx_pool_context_hash[key]; + + while (node) { + + if (node->pool == pool + && node->index == index) { + + + node->data = data; + return NGX_OK; + } + node = node->next; + } + + cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_context_node_t)); + + if (cln == NULL) { + + return NGX_ERROR; + } + + cln->handler = ngx_pool_context_cleanup; + node = cln->data; + + node->prev = NULL; + node->next = NULL; + node->pool = pool; + node->index = index; + node->data = data; + + ngx_pool_context_link(&ngx_pool_context_hash[key], node); + + return NGX_OK; +} + + +static void +ngx_pool_context_cleanup(void *data) +{ + ngx_pool_context_node_t *node = data; + + ngx_pool_context_unlink(node); + +} + + +static void * +ngx_pool_context_create_conf(ngx_cycle_t *cycle) +{ + ngx_pool_context_conf_t *pcf; + + /* create config */ + pcf = ngx_pcalloc(cycle->pool, sizeof(ngx_pool_context_conf_t)); + if (pcf == NULL) { + return NULL; + } + + pcf->size = NGX_CONF_UNSET_UINT; + + return pcf; +} + + +static char * +ngx_pool_context_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_pool_context_conf_t *pcf = conf; + + ngx_conf_init_uint_value(pcf->size, NGX_POOL_CTX_SIZE); + + ngx_pool_context_hash_size = pcf->size; + + ngx_pool_context_hash = ngx_palloc(cycle->pool, sizeof(ngx_pool_context_node_t *) * ngx_pool_context_hash_size); + + if (ngx_pool_context_hash == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + diff --git a/nginx/modsecurity/ngx_pool_context.h b/nginx/modsecurity/ngx_pool_context.h new file mode 100644 index 0000000000..de8b2f3a2e --- /dev/null +++ b/nginx/modsecurity/ngx_pool_context.h @@ -0,0 +1,12 @@ + + +#ifndef _NGX_POOL_CONTEXT_H_INCLUDE_ +#define _NGX_POOL_CONTEXT_H_INCLUDE_ + +void* ngx_pool_get_ctx(ngx_pool_t * pool, ngx_uint_t index); +ngx_int_t ngx_pool_set_ctx(ngx_pool_t * pool, ngx_uint_t index,void * data); + +#define ngx_http_get_module_pool_ctx(r, module) ngx_pool_get_ctx(r->pool, module.index) +#define ngx_http_set_pool_ctx(r, c, module) ngx_pool_set_ctx(r->pool, module.index, c) + +#endif /* _NGX_POOL_CONTEXT_H_INCLUDE_ */ diff --git a/standalone/Makefile.in b/standalone/Makefile.in index 0d23ac0823..ca84e21187 100644 --- a/standalone/Makefile.in +++ b/standalone/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.11.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -16,6 +16,23 @@ @SET_MAKE@ VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ @@ -118,6 +135,11 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ $(LDFLAGS) -o $@ SOURCES = $(standalone_la_SOURCES) DIST_SOURCES = $(standalone_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -395,7 +417,6 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) - test -z "$(pkglibdir)" || $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ @@ -403,6 +424,8 @@ install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) else :; fi; \ done; \ test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } @@ -935,7 +958,17 @@ uninstall-am: uninstall-pkglibLTLIBRARIES install-exec-hook: $(pkglib_LTLIBRARIES) - @echo "Removing unused static libraries..."; \ + @echo "Creating Nginx config file..."; \ + rm -f ../nginx/modsecurity/config; \ + echo "ngx_addon_name=ngx_http_modsecurity" >> ../nginx/modsecurity/config; \ + echo "# HTTP_MODULES=\"\$$HTTP_MODULES ngx_http_modsecurity\"" >> ../nginx/modsecurity/config; \ + echo "HTTP_HEADERS_FILTER_MODULE=\"ngx_http_modsecurity \$$HTTP_HEADERS_FILTER_MODULE\"" >> ../nginx/modsecurity/config; \ + echo "NGX_ADDON_SRCS=\"\$$NGX_ADDON_SRCS \$$ngx_addon_dir/ngx_http_modsecurity.c \$$ngx_addon_dir/apr_bucket_nginx.c\"" >> ../nginx/modsecurity/config;\ + echo "NGX_ADDON_DEPS=\"\$$NGX_ADDON_DEPS\"" >> ../nginx/modsecurity/config; \ + echo "CORE_LIBS=\"\$$CORE_LIBS \$$ngx_addon_dir/../../standalone/.libs/standalone.a -lapr-1 -laprutil-1 -lxml2 -lm @LUA_LDADD@\"" >> ../nginx/modsecurity/config; \ + echo "CORE_INCS=\"\$$CORE_INCS /usr/include/apache2 /usr/include/apr-1.0 /usr/include/httpd /usr/include/apr-1 \$$ngx_addon_dir \$$ngx_addon_dir/../../standalone \$$ngx_addon_dir/../../apache2 /usr/include/libxml2 `echo @LUA_CFLAGS@ | cut -d "I" -f3`\"" >> ../nginx/modsecurity/config; \ + echo "have=REQUEST_EARLY . auto/have" >> ../nginx/modsecurity/config;\ + echo "Removing unused static libraries..."; \ for m in $(pkglib_LTLIBRARIES); do \ base=`echo $$m | sed 's/\..*//'`; \ rm -f $(DESTDIR)$(pkglibdir)/$$base.*a; \