mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-11-22 12:06:05 +03:00
Merge pull request #54 from chaizhenhua/regression-tests
Regression tests
This commit is contained in:
@@ -11,7 +11,8 @@ APR_CFLAGS=""
|
|||||||
APR_CPPFLAGS=""
|
APR_CPPFLAGS=""
|
||||||
APR_LDFLAGS=""
|
APR_LDFLAGS=""
|
||||||
APR_LDADD=""
|
APR_LDADD=""
|
||||||
|
APR_INCLUDEDIR=""
|
||||||
|
APR_LINKLD=""
|
||||||
AC_DEFUN([CHECK_APR],
|
AC_DEFUN([CHECK_APR],
|
||||||
[dnl
|
[dnl
|
||||||
|
|
||||||
@@ -63,6 +64,10 @@ if test -n "${apr_path}"; then
|
|||||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LDFLAGS: $APR_LDFLAGS); fi
|
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LDFLAGS: $APR_LDFLAGS); fi
|
||||||
APR_LDADD="`${APR_CONFIG} --link-libtool`"
|
APR_LDADD="`${APR_CONFIG} --link-libtool`"
|
||||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LDADD: $APR_LDADD); fi
|
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LDADD: $APR_LDADD); fi
|
||||||
|
APR_INCLUDEDIR="`${APR_CONFIG} --includedir`"
|
||||||
|
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr INCLUDEDIR: $APR_INCLUDEDIR); fi
|
||||||
|
APR_LINKLD="`${APR_CONFIG} --link-ld`"
|
||||||
|
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LINKLD: $APR_LINKLD); fi
|
||||||
else
|
else
|
||||||
AC_MSG_RESULT([no])
|
AC_MSG_RESULT([no])
|
||||||
fi
|
fi
|
||||||
@@ -73,6 +78,8 @@ AC_SUBST(APR_CFLAGS)
|
|||||||
AC_SUBST(APR_CPPFLAGS)
|
AC_SUBST(APR_CPPFLAGS)
|
||||||
AC_SUBST(APR_LDFLAGS)
|
AC_SUBST(APR_LDFLAGS)
|
||||||
AC_SUBST(APR_LDADD)
|
AC_SUBST(APR_LDADD)
|
||||||
|
AC_SUBST(APR_INCLUDEDIR)
|
||||||
|
AC_SUBST(APR_LINKLD)
|
||||||
|
|
||||||
if test -z "${APR_VERSION}"; then
|
if test -z "${APR_VERSION}"; then
|
||||||
AC_MSG_NOTICE([*** apr library not found.])
|
AC_MSG_NOTICE([*** apr library not found.])
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ APU_CONFIG=""
|
|||||||
APU_CFLAGS=""
|
APU_CFLAGS=""
|
||||||
APU_LDFLAGS=""
|
APU_LDFLAGS=""
|
||||||
APU_LDADD=""
|
APU_LDADD=""
|
||||||
|
APU_INCLUDEDIR=""
|
||||||
|
APU_LINKLD=""
|
||||||
|
|
||||||
AC_DEFUN([CHECK_APU],
|
AC_DEFUN([CHECK_APU],
|
||||||
[dnl
|
[dnl
|
||||||
@@ -18,7 +20,7 @@ AC_ARG_WITH(
|
|||||||
apu,
|
apu,
|
||||||
[AC_HELP_STRING([--with-apu=PATH],[Path to apu prefix or config script])],
|
[AC_HELP_STRING([--with-apu=PATH],[Path to apu prefix or config script])],
|
||||||
[test_paths="${with_apu}"],
|
[test_paths="${with_apu}"],
|
||||||
[test_paths="/usr/local/libapr-util /usr/local/apr-util /usr/local/libapu /usr/local/apu /usr/local /opt/libapr-util /opt/apr-util /opt/libapu /opt/apu /opt /usr"])
|
[test_paths="/usr/local/libapr-util /usr/local/apr-util /usr/local/libapu /usr/local/apu /usr/local/apr /usr/local /opt/libapr-util /opt/apr-util /opt/libapu /opt/apu /opt /usr"])
|
||||||
|
|
||||||
AC_MSG_CHECKING([for libapu config script])
|
AC_MSG_CHECKING([for libapu config script])
|
||||||
|
|
||||||
@@ -60,6 +62,10 @@ if test -n "${apu_path}"; then
|
|||||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LDFLAGS: $APU_LDFLAGS); fi
|
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LDFLAGS: $APU_LDFLAGS); fi
|
||||||
APU_LDADD="`${APU_CONFIG} --link-libtool`"
|
APU_LDADD="`${APU_CONFIG} --link-libtool`"
|
||||||
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LDADD: $APU_LDADD); fi
|
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LDADD: $APU_LDADD); fi
|
||||||
|
APU_INCLUDEDIR="`${APU_CONFIG} --includedir`"
|
||||||
|
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu INCLUDEDIR: $APU_INCLUDEDIR); fi
|
||||||
|
APU_LINKLD="`${APU_CONFIG} --link-ld`"
|
||||||
|
if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LINKLD: $APU_LINKLD); fi
|
||||||
else
|
else
|
||||||
AC_MSG_RESULT([no])
|
AC_MSG_RESULT([no])
|
||||||
fi
|
fi
|
||||||
@@ -69,6 +75,8 @@ AC_SUBST(APU_VERSION)
|
|||||||
AC_SUBST(APU_CFLAGS)
|
AC_SUBST(APU_CFLAGS)
|
||||||
AC_SUBST(APU_LDFLAGS)
|
AC_SUBST(APU_LDFLAGS)
|
||||||
AC_SUBST(APU_LDADD)
|
AC_SUBST(APU_LDADD)
|
||||||
|
AC_SUBST(APU_INCLUDEDIR)
|
||||||
|
AC_SUBST(APU_LINKLD)
|
||||||
|
|
||||||
if test -z "${APU_VERSION}"; then
|
if test -z "${APU_VERSION}"; then
|
||||||
AC_MSG_NOTICE([*** apu library not found.])
|
AC_MSG_NOTICE([*** apu library not found.])
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ CORE_MODULES="$CORE_MODULES ngx_pool_context_module"
|
|||||||
HTTP_AUX_FILTER_MODULES="ngx_http_modsecurity $HTTP_AUX_FILTER_MODULES"
|
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_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"
|
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_LIBS="$CORE_LIBS $ngx_addon_dir/../../standalone/.libs/standalone.a -L/usr/local/apr/lib -lapr-1 -L/usr/local/apr/lib -laprutil-1 -lpcre -lxml2 -lz -lm -ldl "
|
||||||
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 "
|
CORE_INCS="$CORE_INCS $ngx_addon_dir $ngx_addon_dir/../../standalone $ngx_addon_dir/../../apache2 /usr/include/libxml2 /usr/local/apache2/include /usr/local/apr/include/apr-1 /usr/local/apr/include/apr-1"
|
||||||
|
|||||||
@@ -156,7 +156,8 @@ ngx_pstrdup0(ngx_pool_t *pool, ngx_str_t *src)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline int ngx_http_modsecurity_method_number(unsigned int nginx)
|
static inline int
|
||||||
|
ngx_http_modsecurity_method_number(unsigned int nginx)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
|
* http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
|
||||||
@@ -246,7 +247,7 @@ ngx_http_modsecurity_load_request(ngx_http_request_t *r)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
req->parsed_uri.path = req->path_info;
|
req->parsed_uri.path = (char *)ngx_pstrdup0(r->pool, &r->uri);
|
||||||
req->parsed_uri.is_initialized = 1;
|
req->parsed_uri.is_initialized = 1;
|
||||||
|
|
||||||
str.data = r->port_start;
|
str.data = r->port_start;
|
||||||
@@ -254,7 +255,7 @@ ngx_http_modsecurity_load_request(ngx_http_request_t *r)
|
|||||||
req->parsed_uri.port = ngx_atoi(str.data, str.len);
|
req->parsed_uri.port = ngx_atoi(str.data, str.len);
|
||||||
req->parsed_uri.port_str = (char *)ngx_pstrdup0(r->pool, &str);
|
req->parsed_uri.port_str = (char *)ngx_pstrdup0(r->pool, &str);
|
||||||
|
|
||||||
req->parsed_uri.query = req->args;
|
req->parsed_uri.query = r->args.len ? req->args : NULL;
|
||||||
req->parsed_uri.dns_looked_up = 0;
|
req->parsed_uri.dns_looked_up = 0;
|
||||||
req->parsed_uri.dns_resolved = 0;
|
req->parsed_uri.dns_resolved = 0;
|
||||||
|
|
||||||
@@ -786,6 +787,29 @@ ngx_http_modsecurity_save_headers_out_visitor(void *data, const char *key, const
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_inline ngx_int_t
|
||||||
|
ngx_http_modsecurity_status(ngx_http_request_t *r, int status)
|
||||||
|
{
|
||||||
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: status %d", status);
|
||||||
|
|
||||||
|
if (status == DECLINED || status == APR_SUCCESS) {
|
||||||
|
return NGX_DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nginx known status */
|
||||||
|
if ( (status >= 300 && status < 308) /* 3XX */
|
||||||
|
|| (status >= 400 && status < 417) /* 4XX */
|
||||||
|
|| (status >= 500 && status < 508) /* 5XX */
|
||||||
|
|| (status == NGX_HTTP_CREATED || status == NGX_HTTP_NO_CONTENT) ) {
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* create loc conf struct */
|
/* create loc conf struct */
|
||||||
static void *
|
static void *
|
||||||
ngx_http_modsecurity_create_loc_conf(ngx_conf_t *cf)
|
ngx_http_modsecurity_create_loc_conf(ngx_conf_t *cf)
|
||||||
@@ -952,18 +976,18 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r)
|
|||||||
|
|
||||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: handler");
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: handler");
|
||||||
|
|
||||||
|
/* create / retrive request ctx */
|
||||||
if (r->internal) {
|
if (r->internal) {
|
||||||
/* we have already processed the request headers with previous loc conf */
|
|
||||||
|
|
||||||
/* TODO: do we need update ctx and process headers again? */
|
|
||||||
ctx = ngx_http_get_module_pool_ctx(r, ngx_http_modsecurity);
|
ctx = ngx_http_get_module_pool_ctx(r, ngx_http_modsecurity);
|
||||||
|
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
|
/* we have already processed the request headers */
|
||||||
ngx_http_set_ctx(r, ctx, ngx_http_modsecurity);
|
ngx_http_set_ctx(r, ctx, ngx_http_modsecurity);
|
||||||
return NGX_DECLINED;
|
return NGX_DECLINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: get internel request ctx failed");
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: request pool ctx empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = ngx_http_modsecurity_create_ctx(r);
|
ctx = ngx_http_modsecurity_create_ctx(r);
|
||||||
@@ -978,23 +1002,24 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r)
|
|||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_http_modsecurity_load_request(r);
|
/* load request to request rec */
|
||||||
|
if (ngx_http_modsecurity_load_request(r) != NGX_OK
|
||||||
if (ngx_http_modsecurity_load_headers_in(r) != NGX_OK) {
|
|| ngx_http_modsecurity_load_headers_in(r) != NGX_OK) {
|
||||||
|
|
||||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* processing request headers */
|
/* processing request headers */
|
||||||
rc = modsecProcessRequestHeaders(ctx->req);
|
rc = ngx_http_modsecurity_status(r, modsecProcessRequestHeaders(ctx->req));
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: modsecProcessRequestHeaders %d", rc);
|
|
||||||
|
|
||||||
if (rc == DECLINED) {
|
if (rc != NGX_DECLINED) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
if (modsecIsRequestBodyAccessEnabled(ctx->req)
|
if (r->method == NGX_HTTP_POST
|
||||||
&& r->method == NGX_HTTP_POST) {
|
&& modsecIsRequestBodyAccessEnabled(ctx->req) ) {
|
||||||
|
|
||||||
/* Processing POST request body, should we process PUT? */
|
/* read POST request body, should we process PUT? */
|
||||||
rc = ngx_http_read_client_request_body(r, ngx_http_modsecurity_body_handler);
|
rc = ngx_http_read_client_request_body(r, ngx_http_modsecurity_body_handler);
|
||||||
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
||||||
return rc;
|
return rc;
|
||||||
@@ -1002,28 +1027,9 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r)
|
|||||||
|
|
||||||
return NGX_DONE;
|
return NGX_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* other method */
|
/* other method */
|
||||||
rc = modsecProcessRequestBody(ctx->req);
|
return ngx_http_modsecurity_status(r, 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 */
|
|
||||||
if (rc < NGX_HTTP_SPECIAL_RESPONSE || rc >= 600) {
|
|
||||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK) {
|
|
||||||
|
|
||||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return NGX_DECLINED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1038,19 +1044,12 @@ ngx_http_modsecurity_body_handler(ngx_http_request_t *r)
|
|||||||
ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
|
ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
|
||||||
|
|
||||||
if (ngx_http_modsecurity_load_request_body(r) != NGX_OK) {
|
if (ngx_http_modsecurity_load_request_body(r) != NGX_OK) {
|
||||||
|
|
||||||
return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
|
return ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = modsecProcessRequestBody(ctx->req);
|
rc = ngx_http_modsecurity_status(r, modsecProcessRequestBody(ctx->req));
|
||||||
|
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: modsecProcessRequestBody %d", rc);
|
if (rc != NGX_DECLINED) {
|
||||||
|
|
||||||
if (rc != DECLINED) {
|
|
||||||
/* 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);
|
return ngx_http_finalize_request(r, rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1070,11 +1069,39 @@ static ngx_int_t
|
|||||||
ngx_http_modsecurity_header_filter(ngx_http_request_t *r) {
|
ngx_http_modsecurity_header_filter(ngx_http_request_t *r) {
|
||||||
ngx_http_modsecurity_loc_conf_t *cf;
|
ngx_http_modsecurity_loc_conf_t *cf;
|
||||||
ngx_http_modsecurity_ctx_t *ctx;
|
ngx_http_modsecurity_ctx_t *ctx;
|
||||||
|
const char *location;
|
||||||
|
ngx_table_elt_t *h;
|
||||||
ngx_int_t rc;
|
ngx_int_t rc;
|
||||||
|
|
||||||
|
|
||||||
cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity);
|
cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity);
|
||||||
ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
|
ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
|
||||||
|
|
||||||
|
if (ctx && ctx->complete
|
||||||
|
&& r->err_status >= NGX_HTTP_MOVED_PERMANENTLY
|
||||||
|
&& r->err_status < 308) {
|
||||||
|
|
||||||
|
/* 3XX load redirect location header so that we can do redirec in phase 3,4 */
|
||||||
|
location = apr_table_get(ctx->req->headers_out, "Location");
|
||||||
|
|
||||||
|
if (location == NULL) {
|
||||||
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = ngx_list_push(&r->headers_out.headers);
|
||||||
|
if (h == NULL) {
|
||||||
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
h->hash = 1;
|
||||||
|
h->key.data = (u_char *)"Location";
|
||||||
|
h->key.len = ngx_strlen("Location");
|
||||||
|
h->value.data = (u_char *)location;
|
||||||
|
h->value.len = ngx_strlen(location);
|
||||||
|
|
||||||
|
return ngx_http_next_header_filter(r);
|
||||||
|
}
|
||||||
|
|
||||||
if (r != r->main || !cf->enable || ctx->complete) {
|
if (r != r->main || !cf->enable || ctx->complete) {
|
||||||
return ngx_http_next_header_filter(r);
|
return ngx_http_next_header_filter(r);
|
||||||
}
|
}
|
||||||
@@ -1091,10 +1118,11 @@ ngx_http_modsecurity_header_filter(ngx_http_request_t *r) {
|
|||||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = modsecProcessResponse(ctx->req);
|
rc = ngx_http_modsecurity_status(r, 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 (rc != NGX_DECLINED) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK
|
if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK
|
||||||
|| ngx_http_modsecurity_save_headers_out(r) != NGX_OK) {
|
|| ngx_http_modsecurity_save_headers_out(r) != NGX_OK) {
|
||||||
@@ -1104,13 +1132,6 @@ ngx_http_modsecurity_header_filter(ngx_http_request_t *r) {
|
|||||||
return ngx_http_next_header_filter(r);
|
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;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1142,24 +1163,20 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* last buf has been saved */
|
/* last buf has been saved */
|
||||||
|
|
||||||
ctx->complete = 1;
|
ctx->complete = 1;
|
||||||
modsecSetResponseBrigade(ctx->req, ctx->brigade);
|
modsecSetResponseBrigade(ctx->req, ctx->brigade);
|
||||||
|
|
||||||
// TODO: do we need reload headers_in ?
|
|
||||||
//
|
|
||||||
if (ngx_http_modsecurity_load_headers_in(r) != NGX_OK
|
if (ngx_http_modsecurity_load_headers_in(r) != NGX_OK
|
||||||
|| ngx_http_modsecurity_load_headers_out(r) != NGX_OK) {
|
|| ngx_http_modsecurity_load_headers_out(r) != NGX_OK) {
|
||||||
|
|
||||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = modsecProcessResponse(ctx->req);
|
rc = ngx_http_modsecurity_status(r, 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 (rc != NGX_DECLINED) {
|
||||||
|
return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, rc);
|
||||||
in = NULL;
|
}
|
||||||
|
|
||||||
apr_brigade_length(ctx->brigade, 0, &content_length);
|
apr_brigade_length(ctx->brigade, 0, &content_length);
|
||||||
|
|
||||||
@@ -1189,13 +1206,6 @@ ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
|||||||
return ngx_http_next_body_filter(r, in);
|
return ngx_http_next_body_filter(r, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc < NGX_HTTP_SPECIAL_RESPONSE || rc >= 600) {
|
|
||||||
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static ngx_http_modsecurity_ctx_t *
|
static ngx_http_modsecurity_ctx_t *
|
||||||
ngx_http_modsecurity_create_ctx(ngx_http_request_t *r)
|
ngx_http_modsecurity_create_ctx(ngx_http_request_t *r)
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ standalone_la_LDFLAGS = -no-undefined -module -avoid-version \
|
|||||||
@PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@
|
@PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
standalone_INCS = `echo "@LIBXML2_CFLAGS@ @LUA_CFLAGS@" | sed -n 's/ *-I *\([^ ]*\) /\1 /gp'` \
|
||||||
|
@APXS_INCLUDEDIR@ @APR_INCLUDEDIR@ @APU_INCLUDEDIR@
|
||||||
|
standalone_LIBS = @APR_LINKLD@ @APU_LINKLD@ @APXS_LDFLAGS@ \
|
||||||
|
@PCRE_LDADD@ @LIBXML2_LDADD@ @LUA_LDADD@
|
||||||
install-exec-hook: $(pkglib_LTLIBRARIES)
|
install-exec-hook: $(pkglib_LTLIBRARIES)
|
||||||
@echo "Creating Nginx config file..."; \
|
@echo "Creating Nginx config file..."; \
|
||||||
rm -f ../nginx/modsecurity/config; \
|
rm -f ../nginx/modsecurity/config; \
|
||||||
@@ -80,8 +84,8 @@ install-exec-hook: $(pkglib_LTLIBRARIES)
|
|||||||
echo "HTTP_AUX_FILTER_MODULES=\"ngx_http_modsecurity \$$HTTP_AUX_FILTER_MODULES\"" >> ../nginx/modsecurity/config; \
|
echo "HTTP_AUX_FILTER_MODULES=\"ngx_http_modsecurity \$$HTTP_AUX_FILTER_MODULES\"" >> ../nginx/modsecurity/config; \
|
||||||
echo "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\"" >> ../nginx/modsecurity/config;\
|
echo "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\"" >> ../nginx/modsecurity/config;\
|
||||||
echo "NGX_ADDON_DEPS=\"\$$NGX_ADDON_DEPS \$$ngx_addon_dir/apr_bucket_nginx.h \$$ngx_addon_dir/ngx_pool_context.h\"" >> ../nginx/modsecurity/config; \
|
echo "NGX_ADDON_DEPS=\"\$$NGX_ADDON_DEPS \$$ngx_addon_dir/apr_bucket_nginx.h \$$ngx_addon_dir/ngx_pool_context.h\"" >> ../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_LIBS=\"\$$CORE_LIBS \$$ngx_addon_dir/../../standalone/.libs/standalone.a $(standalone_LIBS) \"" >> ../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 "CORE_INCS=\"\$$CORE_INCS \$$ngx_addon_dir \$$ngx_addon_dir/../../standalone \$$ngx_addon_dir/../../apache2 $(standalone_INCS)\"" >> ../nginx/modsecurity/config; \
|
||||||
echo "Removing unused static libraries..."; \
|
echo "Removing unused static libraries..."; \
|
||||||
for m in $(pkglib_LTLIBRARIES); do \
|
for m in $(pkglib_LTLIBRARIES); do \
|
||||||
base=`echo $$m | sed 's/\..*//'`; \
|
base=`echo $$m | sed 's/\..*//'`; \
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
# Makefile.in generated by automake 1.11.6 from Makefile.am.
|
# Makefile.in generated by automake 1.12.2 from Makefile.am.
|
||||||
# @configure_input@
|
# @configure_input@
|
||||||
|
|
||||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
# Copyright (C) 1994-2012 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
|
# This Makefile.in is free software; the Free Software Foundation
|
||||||
# gives unlimited permission to copy and/or distribute it,
|
# gives unlimited permission to copy and/or distribute it,
|
||||||
# with or without modifications, as long as this notice is preserved.
|
# with or without modifications, as long as this notice is preserved.
|
||||||
@@ -51,7 +50,8 @@ POST_UNINSTALL = :
|
|||||||
build_triplet = @build@
|
build_triplet = @build@
|
||||||
host_triplet = @host@
|
host_triplet = @host@
|
||||||
subdir = standalone
|
subdir = standalone
|
||||||
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
|
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
|
||||||
|
$(top_srcdir)/build/depcomp
|
||||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||||
am__aclocal_m4_deps = $(top_srcdir)/build/find_apr.m4 \
|
am__aclocal_m4_deps = $(top_srcdir)/build/find_apr.m4 \
|
||||||
$(top_srcdir)/build/find_apu.m4 \
|
$(top_srcdir)/build/find_apu.m4 \
|
||||||
@@ -149,13 +149,17 @@ AMTAR = @AMTAR@
|
|||||||
APR_CFLAGS = @APR_CFLAGS@
|
APR_CFLAGS = @APR_CFLAGS@
|
||||||
APR_CONFIG = @APR_CONFIG@
|
APR_CONFIG = @APR_CONFIG@
|
||||||
APR_CPPFLAGS = @APR_CPPFLAGS@
|
APR_CPPFLAGS = @APR_CPPFLAGS@
|
||||||
|
APR_INCLUDEDIR = @APR_INCLUDEDIR@
|
||||||
APR_LDADD = @APR_LDADD@
|
APR_LDADD = @APR_LDADD@
|
||||||
APR_LDFLAGS = @APR_LDFLAGS@
|
APR_LDFLAGS = @APR_LDFLAGS@
|
||||||
|
APR_LINKLD = @APR_LINKLD@
|
||||||
APR_VERSION = @APR_VERSION@
|
APR_VERSION = @APR_VERSION@
|
||||||
APU_CFLAGS = @APU_CFLAGS@
|
APU_CFLAGS = @APU_CFLAGS@
|
||||||
APU_CONFIG = @APU_CONFIG@
|
APU_CONFIG = @APU_CONFIG@
|
||||||
|
APU_INCLUDEDIR = @APU_INCLUDEDIR@
|
||||||
APU_LDADD = @APU_LDADD@
|
APU_LDADD = @APU_LDADD@
|
||||||
APU_LDFLAGS = @APU_LDFLAGS@
|
APU_LDFLAGS = @APU_LDFLAGS@
|
||||||
|
APU_LINKLD = @APU_LINKLD@
|
||||||
APU_VERSION = @APU_VERSION@
|
APU_VERSION = @APU_VERSION@
|
||||||
APXS = @APXS@
|
APXS = @APXS@
|
||||||
APXS_BINDIR = @APXS_BINDIR@
|
APXS_BINDIR = @APXS_BINDIR@
|
||||||
@@ -381,6 +385,12 @@ standalone_la_LIBADD = @APR_LDADD@ @APU_LDADD@ @PCRE_LDADD@ @LIBXML2_LDADD@ @LUA
|
|||||||
@SOLARIS_TRUE@ @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \
|
@SOLARIS_TRUE@ @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \
|
||||||
@SOLARIS_TRUE@ @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@
|
@SOLARIS_TRUE@ @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@
|
||||||
|
|
||||||
|
standalone_INCS = `echo "@LIBXML2_CFLAGS@ @LUA_CFLAGS@" | sed -n 's/ *-I *\([^ ]*\) /\1 /gp'` \
|
||||||
|
@APXS_INCLUDEDIR@ @APR_INCLUDEDIR@ @APU_INCLUDEDIR@
|
||||||
|
|
||||||
|
standalone_LIBS = @APR_LINKLD@ @APU_LINKLD@ @APXS_LDFLAGS@ \
|
||||||
|
@PCRE_LDADD@ @LIBXML2_LDADD@ @LUA_LDADD@
|
||||||
|
|
||||||
all: all-am
|
all: all-am
|
||||||
|
|
||||||
.SUFFIXES:
|
.SUFFIXES:
|
||||||
@@ -441,12 +451,14 @@ uninstall-pkglibLTLIBRARIES:
|
|||||||
|
|
||||||
clean-pkglibLTLIBRARIES:
|
clean-pkglibLTLIBRARIES:
|
||||||
-test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES)
|
-test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES)
|
||||||
@list='$(pkglib_LTLIBRARIES)'; for p in $$list; do \
|
@list='$(pkglib_LTLIBRARIES)'; \
|
||||||
dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
|
locs=`for p in $$list; do echo $$p; done | \
|
||||||
test "$$dir" != "$$p" || dir=.; \
|
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
|
||||||
echo "rm -f \"$${dir}/so_locations\""; \
|
sort -u`; \
|
||||||
rm -f "$${dir}/so_locations"; \
|
test -z "$$locs" || { \
|
||||||
done
|
echo rm -f $${locs}; \
|
||||||
|
rm -f $${locs}; \
|
||||||
|
}
|
||||||
standalone.la: $(standalone_la_OBJECTS) $(standalone_la_DEPENDENCIES) $(EXTRA_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)
|
$(standalone_la_LINK) -rpath $(pkglibdir) $(standalone_la_OBJECTS) $(standalone_la_LIBADD) $(LIBS)
|
||||||
|
|
||||||
@@ -797,6 +809,20 @@ GTAGS:
|
|||||||
&& $(am__cd) $(top_srcdir) \
|
&& $(am__cd) $(top_srcdir) \
|
||||||
&& gtags -i $(GTAGS_ARGS) "$$here"
|
&& gtags -i $(GTAGS_ARGS) "$$here"
|
||||||
|
|
||||||
|
cscopelist: $(HEADERS) $(SOURCES) $(LISP)
|
||||||
|
list='$(SOURCES) $(HEADERS) $(LISP)'; \
|
||||||
|
case "$(srcdir)" in \
|
||||||
|
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
|
||||||
|
*) sdir=$(subdir)/$(srcdir) ;; \
|
||||||
|
esac; \
|
||||||
|
for i in $$list; do \
|
||||||
|
if test -f "$$i"; then \
|
||||||
|
echo "$(subdir)/$$i"; \
|
||||||
|
else \
|
||||||
|
echo "$$sdir/$$i"; \
|
||||||
|
fi; \
|
||||||
|
done >> $(top_builddir)/cscope.files
|
||||||
|
|
||||||
distclean-tags:
|
distclean-tags:
|
||||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||||
|
|
||||||
@@ -942,32 +968,30 @@ uninstall-am: uninstall-pkglibLTLIBRARIES
|
|||||||
.MAKE: install-am install-exec-am install-strip
|
.MAKE: install-am install-exec-am install-strip
|
||||||
|
|
||||||
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
|
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
|
||||||
clean-libtool clean-pkglibLTLIBRARIES ctags distclean \
|
clean-libtool clean-pkglibLTLIBRARIES cscopelist ctags \
|
||||||
distclean-compile distclean-generic distclean-libtool \
|
distclean distclean-compile distclean-generic \
|
||||||
distclean-tags distdir dvi dvi-am html html-am info info-am \
|
distclean-libtool distclean-tags distdir dvi dvi-am html \
|
||||||
install install-am install-data install-data-am install-dvi \
|
html-am info info-am install install-am install-data \
|
||||||
install-dvi-am install-exec install-exec-am install-exec-hook \
|
install-data-am install-dvi install-dvi-am install-exec \
|
||||||
install-html install-html-am install-info install-info-am \
|
install-exec-am install-exec-hook install-html install-html-am \
|
||||||
install-man install-pdf install-pdf-am \
|
install-info install-info-am install-man install-pdf \
|
||||||
install-pkglibLTLIBRARIES install-ps install-ps-am \
|
install-pdf-am install-pkglibLTLIBRARIES install-ps \
|
||||||
install-strip installcheck installcheck-am installdirs \
|
install-ps-am install-strip installcheck installcheck-am \
|
||||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
installdirs maintainer-clean maintainer-clean-generic \
|
||||||
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
|
mostlyclean mostlyclean-compile mostlyclean-generic \
|
||||||
pdf pdf-am ps ps-am tags uninstall uninstall-am \
|
mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
|
||||||
uninstall-pkglibLTLIBRARIES
|
uninstall-am uninstall-pkglibLTLIBRARIES
|
||||||
|
|
||||||
|
|
||||||
install-exec-hook: $(pkglib_LTLIBRARIES)
|
install-exec-hook: $(pkglib_LTLIBRARIES)
|
||||||
@echo "Creating Nginx config file..."; \
|
@echo "Creating Nginx config file..."; \
|
||||||
rm -f ../nginx/modsecurity/config; \
|
rm -f ../nginx/modsecurity/config; \
|
||||||
echo "ngx_addon_name=ngx_http_modsecurity" >> ../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 "CORE_MODULES=\"\$$CORE_MODULES ngx_pool_context_module\"" >> ../nginx/modsecurity/config; \
|
||||||
echo "HTTP_HEADERS_FILTER_MODULE=\"ngx_http_modsecurity \$$HTTP_HEADERS_FILTER_MODULE\"" >> ../nginx/modsecurity/config; \
|
echo "HTTP_AUX_FILTER_MODULES=\"ngx_http_modsecurity \$$HTTP_AUX_FILTER_MODULES\"" >> ../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_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\"" >> ../nginx/modsecurity/config;\
|
||||||
echo "NGX_ADDON_DEPS=\"\$$NGX_ADDON_DEPS\"" >> ../nginx/modsecurity/config; \
|
echo "NGX_ADDON_DEPS=\"\$$NGX_ADDON_DEPS \$$ngx_addon_dir/apr_bucket_nginx.h \$$ngx_addon_dir/ngx_pool_context.h\"" >> ../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_LIBS=\"\$$CORE_LIBS \$$ngx_addon_dir/../../standalone/.libs/standalone.a $(standalone_LIBS) \"" >> ../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 "CORE_INCS=\"\$$CORE_INCS \$$ngx_addon_dir \$$ngx_addon_dir/../../standalone \$$ngx_addon_dir/../../apache2 $(standalone_INCS)\"" >> ../nginx/modsecurity/config; \
|
||||||
echo "have=REQUEST_EARLY . auto/have" >> ../nginx/modsecurity/config;\
|
|
||||||
echo "Removing unused static libraries..."; \
|
echo "Removing unused static libraries..."; \
|
||||||
for m in $(pkglib_LTLIBRARIES); do \
|
for m in $(pkglib_LTLIBRARIES); do \
|
||||||
base=`echo $$m | sed 's/\..*//'`; \
|
base=`echo $$m | sed 's/\..*//'`; \
|
||||||
|
|||||||
141
standalone/api.c
141
standalone/api.c
@@ -223,43 +223,13 @@ apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *bb_out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
apr_status_t ap_http_out_filter(ap_filter_t *f, apr_bucket_brigade *b) {
|
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_status_t rc;
|
||||||
apr_bucket_brigade *bb_out;
|
apr_bucket_brigade *bb_out = (apr_bucket_brigade *)f->ctx;
|
||||||
|
|
||||||
bb_out = modsecGetResponseBrigade(f->r);
|
|
||||||
|
|
||||||
|
|
||||||
if (bb_out) {
|
|
||||||
APR_BRIGADE_CONCAT(bb_out, b);
|
APR_BRIGADE_CONCAT(bb_out, b);
|
||||||
return APR_SUCCESS;
|
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)
|
|
||||||
&& msr->txcfg->resbody_access) {
|
|
||||||
|
|
||||||
if (modsecWriteResponse != NULL) {
|
|
||||||
char *data = NULL;
|
|
||||||
apr_size_t length;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: return ?*/
|
|
||||||
modsecWriteResponse(msr->r, data, msr->stream_output_length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return APR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void modsecTerminate() {
|
void modsecTerminate() {
|
||||||
apr_pool_destroy(pool);
|
apr_pool_destroy(pool);
|
||||||
pool = NULL;
|
pool = NULL;
|
||||||
@@ -551,74 +521,117 @@ int modsecIsResponseBodyAccessEnabled(request_rec *r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int modsecProcessResponse(request_rec *r) {
|
int modsecProcessResponse(request_rec *r) {
|
||||||
int status = DECLINED;
|
int status;
|
||||||
|
modsec_rec *msr;
|
||||||
|
apr_bucket *e;
|
||||||
|
ap_filter_t *f;
|
||||||
|
apr_bucket_brigade *bb_in, *bb_out, *bb;
|
||||||
|
|
||||||
if(r->output_filters != NULL) {
|
if(r->output_filters == NULL) {
|
||||||
modsec_rec *msr = (modsec_rec *)r->output_filters->ctx;
|
return DECLINED;
|
||||||
char buf[8192];
|
}
|
||||||
char *tmp = NULL;
|
|
||||||
apr_bucket *e = NULL;
|
|
||||||
unsigned int readcnt = 0;
|
|
||||||
int is_eos = 0;
|
|
||||||
ap_filter_t *f = NULL;
|
|
||||||
apr_bucket_brigade *bb_in, *bb = NULL;
|
|
||||||
|
|
||||||
|
msr = (modsec_rec *)r->output_filters->ctx;
|
||||||
if (msr == NULL) {
|
if (msr == NULL) {
|
||||||
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
|
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
|
||||||
"ModSecurity: Internal Error: msr is null in output filter.");
|
"ModSecurity: Internal Error: msr is null in output filter.");
|
||||||
ap_remove_output_filter(r->output_filters);
|
ap_remove_output_filter(r->output_filters);
|
||||||
return send_error_bucket(msr, r->output_filters, HTTP_INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
bb = apr_brigade_create(msr->mp, r->connection->bucket_alloc);
|
|
||||||
|
|
||||||
if (bb == NULL) {
|
|
||||||
msr_log(msr, 1, "Process response: Failed to create brigade.");
|
|
||||||
return APR_EGENERAL;
|
return APR_EGENERAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
msr->r = r;
|
msr->r = r;
|
||||||
|
|
||||||
bb_in = modsecGetResponseBrigade(r);
|
/* create input response brigade */
|
||||||
|
bb_in = apr_brigade_create(msr->mp, r->connection->bucket_alloc);
|
||||||
|
|
||||||
if (bb_in != NULL) {
|
if (bb_in == NULL) {
|
||||||
APR_BRIGADE_CONCAT(bb, bb_in);
|
msr_log(msr, 1, "Process response: Failed to create brigade.");
|
||||||
if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
|
return APR_EGENERAL;
|
||||||
e = apr_bucket_eos_create(bb->bucket_alloc);
|
}
|
||||||
APR_BRIGADE_INSERT_TAIL(bb, e);
|
|
||||||
|
/* get input response brigade */
|
||||||
|
bb = modsecGetResponseBrigade(r);
|
||||||
|
if (bb != NULL) {
|
||||||
|
APR_BRIGADE_CONCAT(bb_in, bb);
|
||||||
|
if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb_in))) {
|
||||||
|
e = apr_bucket_eos_create(bb_in->bucket_alloc);
|
||||||
|
APR_BRIGADE_INSERT_TAIL(bb_in, e);
|
||||||
}
|
}
|
||||||
} else if (modsecReadResponse != NULL) {
|
} else if (modsecReadResponse != NULL) {
|
||||||
|
unsigned int readcnt = 0;
|
||||||
|
int is_eos = 0;
|
||||||
|
char buf[8192];
|
||||||
while(!is_eos) {
|
while(!is_eos) {
|
||||||
modsecReadResponse(r, buf, 8192, &readcnt, &is_eos);
|
modsecReadResponse(r, buf, 8192, &readcnt, &is_eos);
|
||||||
|
|
||||||
if(readcnt > 0) {
|
if(readcnt > 0) {
|
||||||
tmp = (char *)apr_palloc(r->pool, readcnt);
|
char *tmp = (char *)apr_palloc(r->pool, readcnt);
|
||||||
memcpy(tmp, buf, readcnt);
|
memcpy(tmp, buf, readcnt);
|
||||||
e = apr_bucket_pool_create(tmp, readcnt, r->pool, r->connection->bucket_alloc);
|
e = apr_bucket_pool_create(tmp, readcnt, r->pool, r->connection->bucket_alloc);
|
||||||
APR_BRIGADE_INSERT_TAIL(bb, e);
|
APR_BRIGADE_INSERT_TAIL(bb_in, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e = apr_bucket_eos_create(r->connection->bucket_alloc);
|
e = apr_bucket_eos_create(r->connection->bucket_alloc);
|
||||||
APR_BRIGADE_INSERT_TAIL(bb, e);
|
APR_BRIGADE_INSERT_TAIL(bb_in, e);
|
||||||
} else {
|
} else {
|
||||||
/* cannot read response body process header only */
|
/* cannot read response body process header only */
|
||||||
|
|
||||||
e = apr_bucket_eos_create(r->connection->bucket_alloc);
|
e = apr_bucket_eos_create(r->connection->bucket_alloc);
|
||||||
APR_BRIGADE_INSERT_TAIL(bb, e);
|
APR_BRIGADE_INSERT_TAIL(bb_in, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
f = ap_add_output_filter("HTTP_OUT", msr, r, r->connection);
|
bb_out = bb ? bb : apr_brigade_create(msr->mp, r->connection->bucket_alloc);
|
||||||
status = ap_pass_brigade(r->output_filters, bb);
|
|
||||||
|
if (bb_out == NULL) {
|
||||||
|
msr_log(msr, 1, "Process response: Failed to create brigade.");
|
||||||
|
return APR_EGENERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* concat output bucket to bb_out */
|
||||||
|
f = ap_add_output_filter("HTTP_OUT", bb_out, r, r->connection);
|
||||||
|
status = ap_pass_brigade(r->output_filters, bb_in);
|
||||||
ap_remove_output_filter(f);
|
ap_remove_output_filter(f);
|
||||||
if(status > 0
|
|
||||||
&& msr->intercept_actionset->intercept_status != 0) {
|
if (status == APR_EGENERAL) {
|
||||||
status = msr->intercept_actionset->intercept_status;
|
/* retrive response status from bb_out */
|
||||||
|
for(e = APR_BRIGADE_FIRST(bb_out);
|
||||||
|
e != APR_BRIGADE_SENTINEL(bb_out);
|
||||||
|
e = APR_BUCKET_NEXT(e)) {
|
||||||
|
if (AP_BUCKET_IS_ERROR(e)) {
|
||||||
|
return ((ap_bucket_error*) e->data)->status;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return APR_EGENERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != DECLINED) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
/* copy bb_out */
|
||||||
|
// is there a way to tell whether the response body was modified or not?
|
||||||
|
if (modsecWriteResponse != NULL
|
||||||
|
&& (msr->txcfg->content_injection_enabled || msr->content_prepend_len != 0 || msr->content_append_len != 0)
|
||||||
|
&& msr->txcfg->resbody_access) {
|
||||||
|
|
||||||
|
char *data = NULL;
|
||||||
|
apr_size_t length;
|
||||||
|
|
||||||
|
status = apr_brigade_pflatten(msr->of_brigade, &data, &length, msr->mp);
|
||||||
|
|
||||||
|
if (status != APR_SUCCESS) {
|
||||||
|
msr_log(msr, 1, "Output filter: Failed to flatten brigade (%d): %s", status,
|
||||||
|
get_apr_error(msr->mp, status));
|
||||||
|
return APR_EGENERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( modsecWriteResponse(msr->r, data, msr->stream_output_length) != APR_SUCCESS) {
|
||||||
|
return APR_EGENERAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DECLINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int modsecFinishRequest(request_rec *r) {
|
int modsecFinishRequest(request_rec *r) {
|
||||||
|
|||||||
0
tests/regression/nginx/conf/empty.conf
Normal file
0
tests/regression/nginx/conf/empty.conf
Normal file
22
tests/regression/nginx/conf/nginx.conf.template
Normal file
22
tests/regression/nginx/conf/nginx.conf.template
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
user root;
|
||||||
|
worker_processes 1;
|
||||||
|
daemon on;
|
||||||
|
error_log logs/error.log debug;
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
ModSecurityEnabled [% enable %];
|
||||||
|
ModSecurityConfig [% config %];
|
||||||
|
server {
|
||||||
|
|
||||||
|
listen [% listen %];
|
||||||
|
server_name localhost;
|
||||||
|
location / {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
736
tests/run-regression-tests-nginx.pl
Normal file
736
tests/run-regression-tests-nginx.pl
Normal file
@@ -0,0 +1,736 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
#
|
||||||
|
# Run regression tests.
|
||||||
|
#
|
||||||
|
# Syntax: run-regression-tests.pl [options] [file [N]]
|
||||||
|
#
|
||||||
|
# All: run-regression-tests.pl
|
||||||
|
# All in file: run-regression-tests.pl file
|
||||||
|
# Nth in file: run-regression-tests.pl file N
|
||||||
|
#
|
||||||
|
use strict;
|
||||||
|
use Time::HiRes qw(gettimeofday sleep);
|
||||||
|
use POSIX qw(WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG);
|
||||||
|
use File::Spec qw(rel2abs);
|
||||||
|
use File::Basename qw(basename dirname);
|
||||||
|
use File::Path qw(make_path);
|
||||||
|
use FileHandle;
|
||||||
|
use IPC::Open2 qw(open2);
|
||||||
|
use IPC::Open3 qw(open3);
|
||||||
|
use Getopt::Std;
|
||||||
|
use Data::Dumper;
|
||||||
|
use IO::Socket;
|
||||||
|
use LWP::UserAgent;
|
||||||
|
use Cwd 'abs_path';
|
||||||
|
use Template;
|
||||||
|
use File::Copy::Recursive qw(dircopy);
|
||||||
|
|
||||||
|
my @TYPES = qw(action config misc rule target);
|
||||||
|
my $SCRIPT = basename($0);
|
||||||
|
my $SCRIPT_DIR = File::Spec->rel2abs(dirname($0));
|
||||||
|
my $REG_DIR = "$SCRIPT_DIR/regression";
|
||||||
|
my $NGINX_DIR = "$REG_DIR/nginx";
|
||||||
|
my $NGINX_CONF_TEMP = "$REG_DIR/nginx/conf/nginx.conf.template";
|
||||||
|
my $NGINX = q(/usr/local/nginx/sbin/nginx);
|
||||||
|
|
||||||
|
my $PASSED = 0;
|
||||||
|
my $TOTAL = 0;
|
||||||
|
my $BUFSIZ = 32768;
|
||||||
|
my %C = ();
|
||||||
|
my %FILE = ();
|
||||||
|
my $UA_NAME = "ModSecurity Regression Tests/1.2.3";
|
||||||
|
my $UA = LWP::UserAgent->new;
|
||||||
|
$UA->agent($UA_NAME);
|
||||||
|
|
||||||
|
$SIG{TERM} = $SIG{INT} = \&handle_interrupt;
|
||||||
|
|
||||||
|
my %opt;
|
||||||
|
getopts('A:E:D:C:T:H:a:p:dvh', \%opt);
|
||||||
|
|
||||||
|
if ($opt{d}) {
|
||||||
|
$Data::Dumper::Indent = 1;
|
||||||
|
$Data::Dumper::Terse = 1;
|
||||||
|
$Data::Dumper::Pad = "";
|
||||||
|
$Data::Dumper::Quotekeys = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub usage {
|
||||||
|
print stderr <<"EOT";
|
||||||
|
@_
|
||||||
|
Usage: $SCRIPT [options] [file [N]]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-P path Specify nginx prefix path (default: $NGINX_DIR)
|
||||||
|
-a file Specify nginx binary (default: $NGINX)
|
||||||
|
-p port Specify nginx port (default: 8088)
|
||||||
|
-v Enable verbose output (details on failure).
|
||||||
|
-d Enable debugging output.
|
||||||
|
-h This help.
|
||||||
|
EOT
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() if ($opt{h});
|
||||||
|
|
||||||
|
### Check nginx binary
|
||||||
|
if (defined $opt{a}) {
|
||||||
|
$NGINX = $opt{a};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$opt{a} = $NGINX;
|
||||||
|
}
|
||||||
|
usage("Invalid Apache startup script: $NGINX\n") unless (-e $NGINX);
|
||||||
|
|
||||||
|
|
||||||
|
### Defaults
|
||||||
|
$opt{P} = "$NGINX_DIR" unless (defined $opt{P});
|
||||||
|
|
||||||
|
my $CONF_DIR = "$opt{P}/conf";
|
||||||
|
my $FILES_DIR = "$opt{P}/logs";
|
||||||
|
my $PID_FILE = "$FILES_DIR/nginx.pid";
|
||||||
|
|
||||||
|
$opt{A} = "$FILES_DIR/modsec_audit.log";
|
||||||
|
$opt{D} = "$FILES_DIR/modsec_debug.log";
|
||||||
|
$opt{E} = "$FILES_DIR/error.log";
|
||||||
|
$opt{C} = "$CONF_DIR/nginx.conf";
|
||||||
|
$opt{p} = 8088 unless (defined $opt{p});
|
||||||
|
$opt{v} = 1 if ($opt{d});
|
||||||
|
|
||||||
|
if ( !-d "$opt{P}" ) {
|
||||||
|
make_path($opt{P}) or die $!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !-d "$opt{P}/logs" ) {
|
||||||
|
make_path("$opt{P}/logs") or die $!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !-d "$opt{P}/html" ) {
|
||||||
|
make_path("$opt{P}/html") or die $!;
|
||||||
|
}
|
||||||
|
|
||||||
|
dircopy("$REG_DIR/server_root/htdocs","$opt{P}/html") or die $!;
|
||||||
|
|
||||||
|
%ENV = (
|
||||||
|
%ENV,
|
||||||
|
$NGINX_DIR => $opt{P},
|
||||||
|
SERVER_PORT => $opt{p},
|
||||||
|
SERVER_NAME => "localhost",
|
||||||
|
# TEST_NGX_PREFIX => $NGINX_DIR,
|
||||||
|
# DATA_DIR => $DATA_DIR,
|
||||||
|
# TEMP_DIR => $TEMP_DIR,
|
||||||
|
# UPLOAD_DIR => $UPLOAD_DIR,
|
||||||
|
CONF_DIR => $CONF_DIR,
|
||||||
|
# MODULES_DIR => $MODULES_DIR,
|
||||||
|
LOGS_DIR => $FILES_DIR,
|
||||||
|
SCRIPT_DIR => $SCRIPT_DIR,
|
||||||
|
REGRESSION_DIR => $REG_DIR,
|
||||||
|
DIST_ROOT => File::Spec->rel2abs(dirname("$SCRIPT_DIR/../../..")),
|
||||||
|
AUDIT_LOG => $opt{A},
|
||||||
|
DEBUG_LOG => $opt{D},
|
||||||
|
ERROR_LOG => $opt{E},
|
||||||
|
NGINX_CONF => $opt{C},
|
||||||
|
# HTDOCS => $opt{H},
|
||||||
|
USER_AGENT => $UA_NAME,
|
||||||
|
);
|
||||||
|
|
||||||
|
#dbg("OPTIONS: ", \%opt);
|
||||||
|
|
||||||
|
if (-e "$PID_FILE") {
|
||||||
|
msg("Shutting down previous instance: $PID_FILE");
|
||||||
|
nginx_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defined $ARGV[0]) {
|
||||||
|
runfile(dirname($ARGV[0]), basename($ARGV[0]), $ARGV[1]);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
for my $type (@TYPES) {
|
||||||
|
my $dir = "$SCRIPT_DIR/regression/$type";
|
||||||
|
my @cfg = ();
|
||||||
|
|
||||||
|
# Get test names
|
||||||
|
opendir(DIR, "$dir") or quit(1, "Failed to open \"$dir\": $!");
|
||||||
|
@cfg = grep { /\.t$/ && -f "$dir/$_" } readdir(DIR);
|
||||||
|
closedir(DIR);
|
||||||
|
|
||||||
|
for my $cfg (sort @cfg) {
|
||||||
|
runfile($dir, $cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
|
||||||
|
|
||||||
|
sub runfile {
|
||||||
|
my($dir, $cfg, $testnum) = @_;
|
||||||
|
my $fn = "$dir/$cfg";
|
||||||
|
my @data = ();
|
||||||
|
my $edata;
|
||||||
|
my @C = ();
|
||||||
|
my @test = ();
|
||||||
|
my $teststr;
|
||||||
|
my $n = 0;
|
||||||
|
my $pass = 0;
|
||||||
|
|
||||||
|
open(CFG, "<$fn") or quit(1, "Failed to open \"$fn\": $!");
|
||||||
|
@data = <CFG>;
|
||||||
|
|
||||||
|
$edata = q/@C = (/ . join("", @data) . q/)/;
|
||||||
|
eval $edata;
|
||||||
|
quit(1, "Failed to read test data \"$cfg\": $@") if ($@);
|
||||||
|
|
||||||
|
unless (@C) {
|
||||||
|
msg("\nNo tests defined for $fn");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg("\nLoaded ".@C." tests from $fn");
|
||||||
|
for my $t (@C) {
|
||||||
|
$n++;
|
||||||
|
next if (defined $testnum and $n != $testnum);
|
||||||
|
|
||||||
|
my $nginx_up = 0;
|
||||||
|
my %t = %{$t || {}};
|
||||||
|
my $id = sprintf("%3d", $n);
|
||||||
|
my $out = "";
|
||||||
|
my $rc = 0;
|
||||||
|
my $conf_fn;
|
||||||
|
|
||||||
|
# Startup nginx with optionally included conf.
|
||||||
|
if (exists $t{conf} and defined $t{conf}) {
|
||||||
|
$conf_fn = sprintf "%s/%s_%s_%06d.conf",
|
||||||
|
$CONF_DIR, $t{type}, $cfg, $n;
|
||||||
|
#dbg("Writing test config to: $conf_fn");
|
||||||
|
open(CONF, ">$conf_fn") or die "Failed to open conf \"$conf_fn\": $!\n";
|
||||||
|
print CONF (ref $t{conf} eq "CODE" ? eval { &{$t{conf}} } : $t{conf});
|
||||||
|
msg("$@") if ($@);
|
||||||
|
close CONF;
|
||||||
|
my %conf=(config => $conf_fn, enable => "on");
|
||||||
|
$nginx_up = nginx_start($t, \%conf) ? 0 : 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$nginx_up = nginx_start($t) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run any prerun setup
|
||||||
|
if ($rc == 0 and exists $t{prerun} and defined $t{prerun}) {
|
||||||
|
vrb("Executing perl prerun...");
|
||||||
|
$rc = &{$t{prerun}};
|
||||||
|
vrb("Perl prerun returned: $rc");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($nginx_up) {
|
||||||
|
# Perform the request and check response
|
||||||
|
if (exists $t{request}) {
|
||||||
|
my $resp = do_request($t{request});
|
||||||
|
if (!$resp) {
|
||||||
|
msg("invalid response");
|
||||||
|
vrb("RESPONSE: ", $resp);
|
||||||
|
$rc = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for my $key (keys %{ $t{match_response} || {}}) {
|
||||||
|
my($neg,$mtype) = ($key =~ m/^(-?)(.*)$/);
|
||||||
|
my $m = $t{match_response}{$key};
|
||||||
|
my $match = match_response($mtype, $resp, $m);
|
||||||
|
if ($neg and defined $match) {
|
||||||
|
$rc = 1;
|
||||||
|
msg("response $mtype matched: $m");
|
||||||
|
vrb($resp);
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
elsif (!$neg and !defined $match) {
|
||||||
|
$rc = 1;
|
||||||
|
msg("response $mtype failed to match: $m");
|
||||||
|
vrb($resp);
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run any arbitrary perl tests
|
||||||
|
if ($rc == 0 and exists $t{test} and defined $t{test}) {
|
||||||
|
dbg("Executing perl test(s)...");
|
||||||
|
$rc = eval { &{$t{test}} };
|
||||||
|
if (! defined $rc) {
|
||||||
|
msg("Error running test: $@");
|
||||||
|
$rc = -1;
|
||||||
|
}
|
||||||
|
dbg("Perl tests returned: $rc");
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search for all log matches
|
||||||
|
if ($rc == 0 and exists $t{match_log} and defined $t{match_log}) {
|
||||||
|
for my $key (keys %{ $t{match_log} || {}}) {
|
||||||
|
my($neg,$mtype) = ($key =~ m/^(-?)(.*)$/);
|
||||||
|
my $m = $t{match_log}{$key};
|
||||||
|
my $match = match_log($mtype, @{$m || []});
|
||||||
|
if ($neg and defined $match) {
|
||||||
|
$rc = 1;
|
||||||
|
msg("$mtype log matched: $m->[0]");
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
elsif (!$neg and !defined $match) {
|
||||||
|
$rc = 1;
|
||||||
|
msg("$mtype log failed to match: $m->[0]");
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search for all file matches
|
||||||
|
if ($rc == 0 and exists $t{match_file} and defined $t{match_file}) {
|
||||||
|
sleep 1; # Make sure the file exists
|
||||||
|
for my $key (keys %{ $t{match_file} || {}}) {
|
||||||
|
my($neg,$fn) = ($key =~ m/^(-?)(.*)$/);
|
||||||
|
my $m = $t{match_file}{$key};
|
||||||
|
my $match = match_file($fn, $m);
|
||||||
|
if ($neg and defined $match) {
|
||||||
|
$rc = 1;
|
||||||
|
msg("$fn file matched: $m");
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
elsif (!$neg and !defined $match) {
|
||||||
|
$rc = 1;
|
||||||
|
msg("$fn file failed match: $m");
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg("Failed to start nginx.");
|
||||||
|
$rc = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rc == 0) {
|
||||||
|
$pass++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vrb("Test Config: $conf_fn");
|
||||||
|
vrb("Debug Log: $FILE{debug}{fn}");
|
||||||
|
dbg(escape("$FILE{debug}{buf}"));
|
||||||
|
vrb("Error Log: $FILE{error}{fn}");
|
||||||
|
dbg(escape("$FILE{error}{buf}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
msg(sprintf("%s) %s%s: %s%s", $id, $t{type}, (exists($t{comment}) ? " - $t{comment}" : ""), ($rc ? "failed" : "passed"), ((defined($out) && $out ne "")? " ($out)" : "")));
|
||||||
|
|
||||||
|
if ($nginx_up) {
|
||||||
|
$nginx_up = nginx_stop(\%t) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$TOTAL += $testnum ? 1 : $n;
|
||||||
|
$PASSED += $pass;
|
||||||
|
|
||||||
|
msg(sprintf("Passed: %2d; Failed: %2d", $pass, $testnum ? (1 - $pass) : ($n - $pass)));
|
||||||
|
}
|
||||||
|
|
||||||
|
# Take out any indenting and translate LF -> CRLF
|
||||||
|
sub normalize_raw_request_data {
|
||||||
|
my $r = $_[0];
|
||||||
|
|
||||||
|
# Allow for indenting in test file
|
||||||
|
$r =~ s/^[ \t]*\x0d?\x0a//s;
|
||||||
|
my($indention) = ($r =~ m/^([ \t]*)/s); # indention taken from first line
|
||||||
|
$r =~ s/^$indention//mg;
|
||||||
|
$r =~ s/(\x0d?\x0a)[ \t]+$/$1/s;
|
||||||
|
|
||||||
|
# Translate LF to CRLF
|
||||||
|
$r =~ s/^\x0a/\x0d\x0a/mg;
|
||||||
|
$r =~ s/([^\x0d])\x0a/$1\x0d\x0a/mg;
|
||||||
|
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_raw_request {
|
||||||
|
my $sock = new IO::Socket::INET(
|
||||||
|
Proto => "tcp",
|
||||||
|
PeerAddr => "localhost",
|
||||||
|
PeerPort => $opt{p},
|
||||||
|
) or msg("Failed to connect to localhost:$opt{p}: $@");
|
||||||
|
return unless ($sock);
|
||||||
|
|
||||||
|
# Join togeather the request
|
||||||
|
my $r = join("", @_);
|
||||||
|
dbg($r);
|
||||||
|
|
||||||
|
# Write to socket
|
||||||
|
print $sock "$r";
|
||||||
|
$sock->shutdown(1);
|
||||||
|
|
||||||
|
# Read from socket
|
||||||
|
my @resp = <$sock>;
|
||||||
|
$sock->close();
|
||||||
|
|
||||||
|
return HTTP::Response->parse(join("", @resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_request {
|
||||||
|
my $r = $_[0];
|
||||||
|
|
||||||
|
# Allow test to execute code
|
||||||
|
if (ref $r eq "CODE") {
|
||||||
|
$r = eval { &$r };
|
||||||
|
msg("$@") unless (defined $r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ref $r eq "HTTP::Request") {
|
||||||
|
my $resp = $UA->request($r);
|
||||||
|
dbg($resp->request()->as_string()) if ($opt{d});
|
||||||
|
return $resp
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return do_raw_request($r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub match_response {
|
||||||
|
my($name, $resp, $re) = @_;
|
||||||
|
|
||||||
|
msg("Warning: Empty regular expression.") if (!defined $re or $re eq "");
|
||||||
|
|
||||||
|
if ($name eq "status") {
|
||||||
|
return $& if ($resp->code =~ m/$re/);
|
||||||
|
}
|
||||||
|
elsif ($name eq "content") {
|
||||||
|
return $& if ($resp->content =~ m/$re/m);
|
||||||
|
}
|
||||||
|
elsif ($name eq "raw") {
|
||||||
|
return $& if ($resp->as_string =~ m/$re/m);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub read_log {
|
||||||
|
my($name, $timeout, $graph) = @_;
|
||||||
|
return match_log($name, undef, $timeout, $graph);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub match_log {
|
||||||
|
my($name, $re, $timeout, $graph) = @_;
|
||||||
|
my $t0 = gettimeofday;
|
||||||
|
my($fh,$rbuf) = ($FILE{$name}{fd}, \$FILE{$name}{buf});
|
||||||
|
my $n = length($$rbuf);
|
||||||
|
my $rc = undef;
|
||||||
|
|
||||||
|
unless (defined $fh) {
|
||||||
|
msg("Error: File \"$name\" is not opened for matching.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timeout = 0 unless (defined $timeout);
|
||||||
|
|
||||||
|
my $i = 0;
|
||||||
|
my $graphed = 0;
|
||||||
|
READ: {
|
||||||
|
do {
|
||||||
|
my $nbytes = $fh->sysread($$rbuf, $BUFSIZ, $n);
|
||||||
|
if (!defined($nbytes)) {
|
||||||
|
msg("Error: Could not read \"$name\" log: $!");
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
elsif (!defined($re) and $nbytes == 0) {
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove APR pool debugging
|
||||||
|
$$rbuf =~ s/POOL DEBUG:[^\n]+PALLOC[^\n]+\n//sg;
|
||||||
|
|
||||||
|
$n = length($$rbuf);
|
||||||
|
|
||||||
|
#dbg("Match \"$re\" in $name \"$$rbuf\" ($n)");
|
||||||
|
if ($$rbuf =~ m/$re/m) {
|
||||||
|
$rc = $&;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
# TODO: Use select()/poll()
|
||||||
|
sleep 0.1 unless ($nbytes == $BUFSIZ);
|
||||||
|
if ($graph and $opt{d}) {
|
||||||
|
$i++;
|
||||||
|
if ($i == 10) {
|
||||||
|
$graphed++;
|
||||||
|
$i=0;
|
||||||
|
print STDERR $graph if ($graphed == 1);
|
||||||
|
print STDERR "."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (gettimeofday - $t0 < $timeout);
|
||||||
|
}
|
||||||
|
print STDERR "\n" if ($graphed);
|
||||||
|
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub match_file {
|
||||||
|
my($neg,$fn) = ($_[0] =~ m/^(-?)(.*)$/);
|
||||||
|
unless (exists $FILE{$fn}) {
|
||||||
|
eval {
|
||||||
|
$FILE{$fn}{fn} = $fn;
|
||||||
|
$FILE{$fn}{fd} = new FileHandle($fn, O_RDONLY) or die "$!\n";
|
||||||
|
$FILE{$fn}{fd}->blocking(0);
|
||||||
|
$FILE{$fn}{buf} = "";
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
msg("Warning: Failed to open file \"$fn\": $@");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match_log($_[0], $_[1]); # timeout makes no sense
|
||||||
|
}
|
||||||
|
|
||||||
|
sub quote_shell {
|
||||||
|
my($s) = @_;
|
||||||
|
return $s unless ($s =~ m|[^\w!%+,\-./:@^]|);
|
||||||
|
$s =~ s/(['\\])/\\$1/g;
|
||||||
|
return "'$s'";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub escape {
|
||||||
|
my @new = ();
|
||||||
|
for my $c (split(//, $_[0])) {
|
||||||
|
my $oc = ord($c);
|
||||||
|
push @new, ((($oc >= 0x20 and $oc <= 0x7e) or $oc == 0x0a or $oc == 0x0d) ? $c : sprintf("\\x%02x", ord($c)));
|
||||||
|
}
|
||||||
|
join('', @new);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub dbg {
|
||||||
|
return unless(@_ and $opt{d});
|
||||||
|
my $out = join "", map {
|
||||||
|
(ref $_ ne "" ? Dumper($_) : $_)
|
||||||
|
} @_;
|
||||||
|
$out =~ s/^/DBG: /mg;
|
||||||
|
print STDOUT "$out\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vrb {
|
||||||
|
return unless(@_ and $opt{v});
|
||||||
|
msg(@_);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub msg {
|
||||||
|
return unless(@_);
|
||||||
|
my $out = join "", map {
|
||||||
|
(ref $_ ne "" ? Dumper($_) : $_)
|
||||||
|
} @_;
|
||||||
|
print STDOUT "$out\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub handle_interrupt {
|
||||||
|
$SIG{TERM} = $SIG{INT} = \&handle_interrupt;
|
||||||
|
|
||||||
|
msg("Interrupted via SIG$_[0]. Shutting down tests...");
|
||||||
|
nginx_stop();
|
||||||
|
|
||||||
|
quit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub quit {
|
||||||
|
my($ec,$msg) = @_;
|
||||||
|
$ec = 0 unless (defined $_[0]);
|
||||||
|
|
||||||
|
msg("$msg") if (defined $msg);
|
||||||
|
|
||||||
|
exit $ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub done {
|
||||||
|
if ($PASSED != $TOTAL) {
|
||||||
|
quit(1, "\n$PASSED/$TOTAL tests passed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
quit(0, "\nAll tests passed ($TOTAL).");
|
||||||
|
}
|
||||||
|
|
||||||
|
sub nginx_stop {
|
||||||
|
my $t = shift;
|
||||||
|
my @p = (
|
||||||
|
$NGINX,
|
||||||
|
-p => $opt{P},
|
||||||
|
-s => "quit",
|
||||||
|
);
|
||||||
|
|
||||||
|
my $nginx_out;
|
||||||
|
my $nginx_pid = open3(undef, $nginx_out, undef, @p) or quit(1);
|
||||||
|
my $out = join("\\n", grep(!/POOL DEBUG/, (<$nginx_out>)));
|
||||||
|
close $nginx_out;
|
||||||
|
waitpid($nginx_pid, 0);
|
||||||
|
|
||||||
|
my $rc = $?;
|
||||||
|
if ( WIFEXITED($rc) ) {
|
||||||
|
$rc = WEXITSTATUS($rc);
|
||||||
|
vrb("Nginx stop returned with $rc.") if ($rc);
|
||||||
|
}
|
||||||
|
elsif( WIFSIGNALED($rc) ) {
|
||||||
|
msg("Nginx stop failed with signal " . WTERMSIG($rc) . ".");
|
||||||
|
$rc = -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg("Nginx stop failed with unknown error.");
|
||||||
|
$rc = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep 0.5;
|
||||||
|
if (-e $PID_FILE) {
|
||||||
|
msg("Nginx stop failed: $PID_FILE still exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub nginx_reset_fd {
|
||||||
|
my($t) = @_;
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
for my $key (keys %FILE) {
|
||||||
|
if (exists $FILE{$key}{fd} and defined $FILE{$key}{fd}) {
|
||||||
|
$FILE{$key}{fd}->close();
|
||||||
|
}
|
||||||
|
delete $FILE{$key};
|
||||||
|
}
|
||||||
|
|
||||||
|
# Error
|
||||||
|
eval {
|
||||||
|
$FILE{error}{fn} = $opt{E};
|
||||||
|
$FILE{error}{fd} = new FileHandle($opt{E}, O_RDWR|O_CREAT) or die "$!\n";
|
||||||
|
$FILE{error}{fd}->blocking(0);
|
||||||
|
$FILE{error}{fd}->sysseek(0, 2);
|
||||||
|
$FILE{error}{buf} = "";
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
msg("Warning: Failed to open file \"$opt{E}\": $@");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Audit
|
||||||
|
eval {
|
||||||
|
$FILE{audit}{fn} = $opt{A};
|
||||||
|
$FILE{audit}{fd} = new FileHandle($opt{A}, O_RDWR|O_CREAT) or die "$!\n";
|
||||||
|
$FILE{audit}{fd}->blocking(0);
|
||||||
|
$FILE{audit}{fd}->sysseek(0, 2);
|
||||||
|
$FILE{audit}{buf} = "";
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
msg("Warning: Failed to open file \"$opt{A}\": $@");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
eval {
|
||||||
|
$FILE{debug}{fn} = $opt{D};
|
||||||
|
$FILE{debug}{fd} = new FileHandle($opt{D}, O_RDWR|O_CREAT) or die "$!\n";
|
||||||
|
$FILE{debug}{fd}->blocking(0);
|
||||||
|
$FILE{debug}{fd}->sysseek(0, 2);
|
||||||
|
$FILE{debug}{buf} = "";
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
msg("Warning: Failed to open file \"$opt{D}\": $@");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Any extras listed in "match_log"
|
||||||
|
if ($t and exists $t->{match_log}) {
|
||||||
|
for my $k (keys %{ $t->{match_log} || {} }) {
|
||||||
|
my($neg,$fn) = ($k =~ m/^(-?)(.*)$/);
|
||||||
|
next if (!$fn or exists $FILE{$fn});
|
||||||
|
eval {
|
||||||
|
$FILE{$fn}{fn} = $fn;
|
||||||
|
$FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT) or die "$!\n";
|
||||||
|
$FILE{$fn}{fd}->blocking(0);
|
||||||
|
$FILE{$fn}{fd}->sysseek(0, 2);
|
||||||
|
$FILE{$fn}{buf} = "";
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
msg("Warning: Failed to open file \"$fn\": $@");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub encode_chunked {
|
||||||
|
my($data, $size) = @_;
|
||||||
|
$size = 128 unless ($size);
|
||||||
|
my $chunked = "";
|
||||||
|
|
||||||
|
my $n = 0;
|
||||||
|
my $bytes = length($data);
|
||||||
|
while ($bytes >= $size) {
|
||||||
|
$chunked .= sprintf "%x\x0d\x0a%s\x0d\x0a", $size, substr($data, $n, $size);
|
||||||
|
$n += $size;
|
||||||
|
$bytes -= $size;
|
||||||
|
}
|
||||||
|
if ($bytes) {
|
||||||
|
$chunked .= sprintf "%x\x0d\x0a%s\x0d\x0a", $bytes, substr($data, $n, $bytes);
|
||||||
|
}
|
||||||
|
$chunked .= "0\x0d\x0a\x0d\x0a"
|
||||||
|
}
|
||||||
|
|
||||||
|
sub nginx_start {
|
||||||
|
my ($t) = shift;
|
||||||
|
my($C) = shift;
|
||||||
|
|
||||||
|
my %conf = (
|
||||||
|
listen => "$opt{p}",
|
||||||
|
config => "$REG_DIR/nginx/conf/empty.conf",
|
||||||
|
enable => "off",
|
||||||
|
);
|
||||||
|
|
||||||
|
while(my($k,$v)= each %$C){
|
||||||
|
$conf{$k}=$v;
|
||||||
|
}
|
||||||
|
|
||||||
|
my ($tt) = Template->new(INCLUDE_PATH => "$REG_DIR/nginx/conf/");
|
||||||
|
my ($output);
|
||||||
|
$tt->process("nginx.conf.template", \%conf, \$output) || die $tt->error;
|
||||||
|
|
||||||
|
open (OUTFILE, ">$opt{C}");
|
||||||
|
print OUTFILE "$output";
|
||||||
|
close(OUTFILE);
|
||||||
|
|
||||||
|
nginx_reset_fd($t);
|
||||||
|
|
||||||
|
my @p = ($NGINX, -p => $opt{P});
|
||||||
|
|
||||||
|
my $nginx_out;
|
||||||
|
my $nginx_pid = open3(undef, $nginx_out, undef, @p) or quit(1);
|
||||||
|
my $out = join("\\n", grep(!/POOL DEBUG/, (<$nginx_out>)));
|
||||||
|
close $nginx_out;
|
||||||
|
waitpid($nginx_pid, 0);
|
||||||
|
|
||||||
|
my $rc = $?;
|
||||||
|
if ( WIFEXITED($rc) ) {
|
||||||
|
$rc = WEXITSTATUS($rc);
|
||||||
|
vrb("Nginx start returned with $rc.") if ($rc);
|
||||||
|
}
|
||||||
|
elsif( WIFSIGNALED($rc) ) {
|
||||||
|
msg("Nginx start failed with signal " . WTERMSIG($rc) . ".");
|
||||||
|
$rc = -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg("Nginx start failed with unknown error.");
|
||||||
|
$rc = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Look for startup msg
|
||||||
|
# unless (defined match_log("error", qr/start worker process/, 60, "Waiting on nginx to start: ")) {
|
||||||
|
# vrb(join(" ", map { quote_shell($_) } @p));
|
||||||
|
# vrb(match_log("error", qr/(^.*ModSecurity: .*)/sm, 10));
|
||||||
|
# msg("Nginx server failed to start.");
|
||||||
|
# nginx_stop();
|
||||||
|
# return -1;
|
||||||
|
# }
|
||||||
|
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user