2148 lines
69 KiB
C
2148 lines
69 KiB
C
// SPDX-FileCopyrightText: 2016-2019, Thibault 'bui' Koechlin <tko@nbs-system.com>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
/*
|
|
** This files contains skeleton functions,
|
|
** such as registered handlers. Readers already
|
|
** aware of nginx's modules can skip most of this.
|
|
*/
|
|
|
|
#include <ngx_config.h>
|
|
|
|
#include <naxsi.h>
|
|
#include <naxsi_net.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#ifndef _WIN32
|
|
#include <strings.h>
|
|
#include <sys/times.h>
|
|
#else
|
|
#include <process.h>
|
|
#endif // !_WIN32
|
|
|
|
#define NAXSI_FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
|
|
|
/*
|
|
** Macro used to print incorrect configuration lines
|
|
*/
|
|
#define ngx_http_naxsi_line_conf_error(cf, value) \
|
|
do { \
|
|
ngx_conf_log_error(NGX_LOG_EMERG, \
|
|
cf, \
|
|
0, \
|
|
"Naxsi-Config : Incorrect line %V %V (%s:%d)...", \
|
|
&(value[0]), \
|
|
&(value[1]), \
|
|
NAXSI_FILENAME, \
|
|
__LINE__); \
|
|
} while (0)
|
|
|
|
/*
|
|
** Module's registered function/handlers.
|
|
*/
|
|
static ngx_int_t
|
|
ngx_http_naxsi_access_handler(ngx_http_request_t* r);
|
|
static ngx_int_t
|
|
ngx_http_naxsi_push_loc_conf(ngx_conf_t* cf, ngx_http_naxsi_loc_conf_t* conf);
|
|
static char*
|
|
ngx_http_naxsi_read_main_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
|
|
static ngx_int_t
|
|
ngx_http_naxsi_init(ngx_conf_t* cf);
|
|
static char*
|
|
ngx_http_naxsi_br_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
|
|
|
|
static char*
|
|
ngx_http_naxsi_cr_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
|
|
|
|
static char*
|
|
ngx_http_naxsi_ud_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_add_ii_loc_conf(ngx_conf_t* cf, ngx_http_naxsi_loc_conf_t* conf, char* ip_str);
|
|
static char*
|
|
ngx_http_naxsi_ii_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
|
|
static char*
|
|
ngx_http_naxsi_ic_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
|
|
|
|
static char*
|
|
ngx_http_naxsi_flags_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
|
|
|
|
static void*
|
|
ngx_http_naxsi_create_loc_conf(ngx_conf_t* cf);
|
|
static char*
|
|
ngx_http_naxsi_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child);
|
|
void*
|
|
ngx_http_naxsi_create_main_conf(ngx_conf_t* cf);
|
|
void
|
|
ngx_http_naxsi_payload_handler(ngx_http_request_t* r);
|
|
|
|
static char*
|
|
ngx_http_naxsi_log_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t* alcf = conf;
|
|
return ngx_log_set_log(cf, &alcf->log);
|
|
}
|
|
|
|
static ngx_http_request_ctx_t*
|
|
recover_request_ctx(ngx_http_request_t* r);
|
|
|
|
static void
|
|
ngx_http_module_cleanup_handler(void* data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_add_variables(ngx_conf_t* cf);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_server_variable(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_uri_variable(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_learning_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_block_variable(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_total_processed_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_total_blocked_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_score_variable(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_match_variable(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_attack_family_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_attack_action_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data);
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_request_id_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data);
|
|
|
|
/* command handled by the module */
|
|
static ngx_command_t ngx_http_naxsi_commands[] = {
|
|
/* BasicRule (in main) */
|
|
{ ngx_string(TOP_MAIN_BASIC_RULE_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_read_main_conf,
|
|
NGX_HTTP_MAIN_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* BasicRule (in main) - nginx style */
|
|
{ ngx_string(TOP_MAIN_BASIC_RULE_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_read_main_conf,
|
|
NGX_HTTP_MAIN_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* BasicRule (in loc) */
|
|
{ ngx_string(TOP_BASIC_RULE_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_br_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* BasicRule (in loc) - nginx style */
|
|
{ ngx_string(TOP_BASIC_RULE_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_br_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* DeniedUrl */
|
|
{ ngx_string(TOP_DENIED_URL_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_ud_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* DeniedUrl - nginx style */
|
|
{ ngx_string(TOP_DENIED_URL_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_ud_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* WhitelistIP */
|
|
{ ngx_string(TOP_IGNORE_IP_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_ii_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* WhitelistIP - nginx style */
|
|
{ ngx_string(TOP_IGNORE_IP_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_ii_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* WhitelistCIDR */
|
|
{ ngx_string(TOP_IGNORE_CIDR_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_ic_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* WhitelistCIDR - nginx style */
|
|
{ ngx_string(TOP_IGNORE_CIDR_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_ic_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* CheckRule */
|
|
{ ngx_string(TOP_CHECK_RULE_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_cr_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* CheckRule - nginx style*/
|
|
{ ngx_string(TOP_CHECK_RULE_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_cr_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
/*
|
|
** flag rules
|
|
*/
|
|
|
|
/* Learning Flag */
|
|
{ ngx_string(TOP_LEARNING_FLAG_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF |
|
|
NGX_CONF_NOARGS,
|
|
ngx_http_naxsi_flags_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* Learning Flag (nginx style) */
|
|
{ ngx_string(TOP_LEARNING_FLAG_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF |
|
|
NGX_CONF_NOARGS,
|
|
ngx_http_naxsi_flags_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* EnableFlag */
|
|
{ ngx_string(TOP_ENABLED_FLAG_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF |
|
|
NGX_CONF_NOARGS,
|
|
ngx_http_naxsi_flags_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* EnableFlag (nginx style) */
|
|
{ ngx_string(TOP_ENABLED_FLAG_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF |
|
|
NGX_CONF_NOARGS,
|
|
ngx_http_naxsi_flags_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* DisableFlag */
|
|
{ ngx_string(TOP_DISABLED_FLAG_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF |
|
|
NGX_CONF_NOARGS,
|
|
ngx_http_naxsi_flags_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* DisableFlag (nginx style) */
|
|
{ ngx_string(TOP_DISABLED_FLAG_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF |
|
|
NGX_CONF_NOARGS,
|
|
ngx_http_naxsi_flags_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* LibInjectionSql */
|
|
{ ngx_string(TOP_LIBINJECTION_SQL_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF |
|
|
NGX_CONF_NOARGS,
|
|
ngx_http_naxsi_flags_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* LibInjectionSql (nginx style) */
|
|
{ ngx_string(TOP_LIBINJECTION_SQL_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF |
|
|
NGX_CONF_NOARGS,
|
|
ngx_http_naxsi_flags_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* LibInjectionXss */
|
|
{ ngx_string(TOP_LIBINJECTION_XSS_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF |
|
|
NGX_CONF_NOARGS,
|
|
ngx_http_naxsi_flags_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* LibInjectionXss (nginx style) */
|
|
{ ngx_string(TOP_LIBINJECTION_XSS_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF |
|
|
NGX_CONF_NOARGS,
|
|
ngx_http_naxsi_flags_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* NaxsiLogfile */
|
|
{ ngx_string(TOP_NAXSI_LOGFILE_T),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_log_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
/* NaxsiLogfile - nginx style*/
|
|
{ ngx_string(TOP_NAXSI_LOGFILE_N),
|
|
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE,
|
|
ngx_http_naxsi_log_loc_conf,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
ngx_null_command
|
|
};
|
|
|
|
static ngx_http_variable_t ngx_http_naxsi_variables[] = {
|
|
{ ngx_string("naxsi_server"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_server_variable, /* Get handler */
|
|
0, /* Data */
|
|
0, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_string("naxsi_uri"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_uri_variable, /* Get handler */
|
|
0, /* Data */
|
|
0, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_string("naxsi_learning"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_learning_variable, /* Get handler */
|
|
0, /* Data */
|
|
0, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_string("naxsi_block"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_block_variable, /* Get handler */
|
|
0, /* Data */
|
|
NGX_HTTP_VAR_NOCACHEABLE, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_string("naxsi_total_processed"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_total_processed_variable, /* Get handler */
|
|
0, /* Data */
|
|
NGX_HTTP_VAR_NOCACHEABLE, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_string("naxsi_total_blocked"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_total_blocked_variable, /* Get handler */
|
|
0, /* Data */
|
|
NGX_HTTP_VAR_NOCACHEABLE, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_string("naxsi_score"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_score_variable, /* Get handler */
|
|
0, /* Data */
|
|
NGX_HTTP_VAR_NOCACHEABLE, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_string("naxsi_match"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_match_variable, /* Get handler */
|
|
0, /* Data */
|
|
NGX_HTTP_VAR_NOCACHEABLE, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_string("naxsi_attack_family"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_attack_family_variable, /* Get handler */
|
|
0, /* Data */
|
|
NGX_HTTP_VAR_NOCACHEABLE, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_string("naxsi_attack_action"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_attack_action_variable, /* Get handler */
|
|
0, /* Data */
|
|
NGX_HTTP_VAR_NOCACHEABLE, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_string("naxsi_request_id"), /* Name */
|
|
NULL, /* Set handler */
|
|
ngx_http_naxsi_request_id_variable, /* Get handler */
|
|
0, /* Data */
|
|
0, /* Flags */
|
|
0 }, /* Index */
|
|
|
|
{ ngx_null_string, NULL, NULL, 0, 0, 0 } /* Sentinel */
|
|
};
|
|
|
|
/*
|
|
** handlers for configuration phases of the module
|
|
*/
|
|
|
|
static ngx_http_module_t ngx_http_naxsi_module_ctx = {
|
|
ngx_http_naxsi_add_variables, /* preconfiguration */
|
|
ngx_http_naxsi_init, /* postconfiguration */
|
|
ngx_http_naxsi_create_main_conf, /* create main configuration */
|
|
NULL, /* init main configuration */
|
|
NULL, /* create server configuration */
|
|
NULL, /* merge server configuration */
|
|
ngx_http_naxsi_create_loc_conf, /* create location configuration */
|
|
ngx_http_naxsi_merge_loc_conf /* merge location configuration */
|
|
};
|
|
|
|
ngx_module_t ngx_http_naxsi_module = { NGX_MODULE_V1,
|
|
&ngx_http_naxsi_module_ctx, /* module context */
|
|
ngx_http_naxsi_commands, /* module directives */
|
|
NGX_HTTP_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 DEFAULT_MAX_LOC_T 10
|
|
|
|
void*
|
|
ngx_http_naxsi_create_main_conf(ngx_conf_t* cf)
|
|
{
|
|
ngx_http_naxsi_main_conf_t* mc;
|
|
|
|
mc = ngx_pcalloc(cf->pool, sizeof(ngx_http_naxsi_main_conf_t));
|
|
if (!mc)
|
|
return (NGX_CONF_ERROR); /*LCOV_EXCL_LINE*/
|
|
mc->locations = ngx_array_create(cf->pool, DEFAULT_MAX_LOC_T, sizeof(ngx_http_naxsi_loc_conf_t*));
|
|
if (!mc->locations)
|
|
return (NGX_CONF_ERROR); /*LCOV_EXCL_LINE*/
|
|
return (mc);
|
|
}
|
|
|
|
/* create log conf struct */
|
|
static void*
|
|
ngx_http_naxsi_create_loc_conf(ngx_conf_t* cf)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t* conf;
|
|
|
|
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_naxsi_loc_conf_t));
|
|
if (conf == NULL)
|
|
return NULL;
|
|
return (conf);
|
|
}
|
|
|
|
/* Push loc conf to main conf */
|
|
static ngx_int_t
|
|
ngx_http_naxsi_push_loc_conf(ngx_conf_t* cf, ngx_http_naxsi_loc_conf_t* conf)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t** bar;
|
|
ngx_http_naxsi_main_conf_t* main_cf;
|
|
|
|
if (!conf->pushed) {
|
|
main_cf = ngx_http_conf_get_module_main_conf(cf, ngx_http_naxsi_module);
|
|
bar = ngx_array_push(main_cf->locations);
|
|
if (!bar)
|
|
return (NGX_ERROR);
|
|
*bar = conf;
|
|
conf->pushed = 1;
|
|
}
|
|
return (NGX_OK);
|
|
}
|
|
|
|
/* merge loc conf */
|
|
/* NOTE/WARNING : This function wasn't tested correctly.
|
|
Actually, we shouldn't merge anything, as configuration is
|
|
specific 'per' location ? */
|
|
static char*
|
|
ngx_http_naxsi_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t* prev = parent;
|
|
ngx_http_naxsi_loc_conf_t* conf = child;
|
|
|
|
if (conf->get_rules == NULL)
|
|
conf->get_rules = prev->get_rules;
|
|
if (conf->raw_body_rules == NULL)
|
|
conf->raw_body_rules = prev->raw_body_rules;
|
|
if (conf->whitelist_rules == NULL)
|
|
conf->whitelist_rules = prev->whitelist_rules;
|
|
if (conf->check_rules == NULL)
|
|
conf->check_rules = prev->check_rules;
|
|
if (conf->body_rules == NULL)
|
|
conf->body_rules = prev->body_rules;
|
|
if (conf->header_rules == NULL)
|
|
conf->header_rules = prev->header_rules;
|
|
if (conf->generic_rules == NULL)
|
|
conf->generic_rules = prev->generic_rules;
|
|
if (conf->tmp_wlr == NULL)
|
|
conf->tmp_wlr = prev->tmp_wlr;
|
|
if (conf->rxmz_wlr == NULL)
|
|
conf->rxmz_wlr = prev->rxmz_wlr;
|
|
if (conf->wlr_url_hash == NULL)
|
|
conf->wlr_url_hash = prev->wlr_url_hash;
|
|
if (conf->wlr_args_hash == NULL)
|
|
conf->wlr_args_hash = prev->wlr_args_hash;
|
|
if (conf->wlr_body_hash == NULL)
|
|
conf->wlr_body_hash = prev->wlr_body_hash;
|
|
if (conf->wlr_headers_hash == NULL)
|
|
conf->wlr_headers_hash = prev->wlr_headers_hash;
|
|
if (conf->ignore_ips == NULL)
|
|
conf->ignore_ips = prev->ignore_ips;
|
|
if (conf->ignore_ips_ha.hsize == 0)
|
|
conf->ignore_ips_ha = prev->ignore_ips_ha;
|
|
if (conf->ignore_cidrs == NULL)
|
|
conf->ignore_cidrs = prev->ignore_cidrs;
|
|
if (conf->disabled_rules == NULL)
|
|
conf->disabled_rules = prev->disabled_rules;
|
|
|
|
if (conf->error == 0)
|
|
conf->error = prev->error;
|
|
if (conf->persistant_data == NULL)
|
|
conf->persistant_data = prev->persistant_data;
|
|
if (conf->extensive == 0)
|
|
conf->extensive = prev->extensive;
|
|
if (conf->learning == 0)
|
|
conf->learning = prev->learning;
|
|
if (conf->enabled == 0)
|
|
conf->enabled = prev->enabled;
|
|
if (conf->force_disabled == 0)
|
|
conf->force_disabled = prev->force_disabled;
|
|
if (conf->libinjection_sql_enabled == 0)
|
|
conf->libinjection_sql_enabled = prev->libinjection_sql_enabled;
|
|
if (conf->libinjection_xss_enabled == 0)
|
|
conf->libinjection_xss_enabled = prev->libinjection_xss_enabled;
|
|
if (conf->denied_url == NULL)
|
|
conf->denied_url = prev->denied_url;
|
|
if (conf->flag_enable_h == 0)
|
|
conf->flag_enable_h = prev->flag_enable_h;
|
|
if (conf->flag_learning_h == 0)
|
|
conf->flag_learning_h = prev->flag_learning_h;
|
|
if (conf->flag_post_action_h == 0)
|
|
conf->flag_post_action_h = prev->flag_post_action_h;
|
|
if (conf->flag_extensive_log_h == 0)
|
|
conf->flag_extensive_log_h = prev->flag_extensive_log_h;
|
|
if (conf->flag_json_log_h == 0)
|
|
conf->flag_json_log_h = prev->flag_json_log_h;
|
|
if (conf->flag_libinjection_xss_h == 0)
|
|
conf->flag_libinjection_xss_h = prev->flag_libinjection_xss_h;
|
|
if (conf->flag_libinjection_sql_h == 0)
|
|
conf->flag_libinjection_sql_h = prev->flag_libinjection_sql_h;
|
|
if (conf->log == NULL)
|
|
conf->log = prev->log;
|
|
|
|
if (ngx_http_naxsi_push_loc_conf(cf, conf) != NGX_OK)
|
|
return NGX_CONF_ERROR;
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
/*
|
|
** This function sets up handlers for ACCESS_PHASE,
|
|
** and will call the hashtable creation function
|
|
** (whitelist aggregation)
|
|
*/
|
|
|
|
extern ngx_http_rule_t nx_int__libinject_sql;
|
|
extern ngx_http_rule_t nx_int__libinject_xss;
|
|
|
|
/* static hash for RT_REQUEST_ID string */
|
|
static ngx_uint_t request_id_h;
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_init(ngx_conf_t* cf)
|
|
{
|
|
ngx_http_handler_pt* h;
|
|
ngx_http_core_main_conf_t* cmcf;
|
|
ngx_http_naxsi_main_conf_t* main_cf;
|
|
ngx_http_naxsi_loc_conf_t** loc_cf;
|
|
unsigned int i;
|
|
|
|
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
|
main_cf = ngx_http_conf_get_module_main_conf(cf, ngx_http_naxsi_module);
|
|
if (cmcf == NULL || main_cf == NULL)
|
|
return (NGX_ERROR); /*LCOV_EXCL_LINE*/
|
|
|
|
/* Register for rewrite phase */
|
|
h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);
|
|
if (h == NULL)
|
|
return (NGX_ERROR); /*LCOV_EXCL_LINE*/
|
|
|
|
*h = ngx_http_naxsi_access_handler;
|
|
/* Go with each locations registered in the srv_conf. */
|
|
loc_cf = main_cf->locations->elts;
|
|
|
|
for (i = 0; i < main_cf->locations->nelts; i++) {
|
|
if (loc_cf[i]->enabled && (!loc_cf[i]->denied_url || loc_cf[i]->denied_url->len <= 0)) {
|
|
/* LCOV_EXCL_START */
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Missing DeniedURL, abort.");
|
|
return (NGX_ERROR);
|
|
/* LCOV_EXCL_STOP */
|
|
}
|
|
|
|
loc_cf[i]->flag_enable_h = ngx_hash_key_lc((u_char*)RT_ENABLE, strlen(RT_ENABLE));
|
|
loc_cf[i]->flag_learning_h = ngx_hash_key_lc((u_char*)RT_LEARNING, strlen(RT_LEARNING));
|
|
loc_cf[i]->flag_post_action_h =
|
|
ngx_hash_key_lc((u_char*)RT_POST_ACTION, strlen(RT_POST_ACTION));
|
|
loc_cf[i]->flag_extensive_log_h =
|
|
ngx_hash_key_lc((u_char*)RT_EXTENSIVE_LOG, strlen(RT_EXTENSIVE_LOG));
|
|
loc_cf[i]->flag_json_log_h = ngx_hash_key_lc((u_char*)RT_JSON_LOG, strlen(RT_JSON_LOG));
|
|
loc_cf[i]->flag_libinjection_xss_h =
|
|
ngx_hash_key_lc((u_char*)RT_LIBINJECTION_XSS, strlen(RT_LIBINJECTION_XSS));
|
|
loc_cf[i]->flag_libinjection_sql_h =
|
|
ngx_hash_key_lc((u_char*)RT_LIBINJECTION_SQL, strlen(RT_LIBINJECTION_SQL));
|
|
|
|
if (ngx_http_naxsi_create_hashtables_n(loc_cf[i], cf) != NGX_OK) {
|
|
/* LCOV_EXCL_START */
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "WhiteList Hash building failed");
|
|
return (NGX_ERROR);
|
|
/* LCOV_EXCL_STOP */
|
|
}
|
|
}
|
|
|
|
request_id_h = ngx_hash_key_lc((u_char*)RT_REQUEST_ID, strlen(RT_REQUEST_ID));
|
|
|
|
/* initialize prng (used for fragmented logs) */
|
|
#ifndef _WIN32
|
|
srandom(time(0) * getpid());
|
|
#else // _WIN32
|
|
srand(time(0) * _getpid());
|
|
#endif // !_WIN32
|
|
|
|
/*
|
|
** initalise internal rules for libinjection sqli/xss
|
|
** (needs proper special scores)
|
|
*/
|
|
nx_int__libinject_sql.sscores = ngx_array_create(cf->pool, 2, sizeof(ngx_http_special_score_t));
|
|
nx_int__libinject_xss.sscores = ngx_array_create(cf->pool, 2, sizeof(ngx_http_special_score_t));
|
|
if (!nx_int__libinject_sql.sscores || !nx_int__libinject_xss.sscores)
|
|
return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
|
/* libinjection sqli/xss - special score init */
|
|
ngx_http_special_score_t* libjct_sql = ngx_array_push(nx_int__libinject_sql.sscores);
|
|
ngx_http_special_score_t* libjct_xss = ngx_array_push(nx_int__libinject_xss.sscores);
|
|
if (!libjct_sql || !libjct_xss)
|
|
return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
|
libjct_sql->sc_tag = ngx_pcalloc(cf->pool, sizeof(ngx_str_t));
|
|
libjct_xss->sc_tag = ngx_pcalloc(cf->pool, sizeof(ngx_str_t));
|
|
if (!libjct_sql->sc_tag || !libjct_xss->sc_tag)
|
|
return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
|
libjct_sql->sc_tag->data = ngx_pcalloc(cf->pool, 18 /* LIBINJECTION_SQL */);
|
|
libjct_xss->sc_tag->data = ngx_pcalloc(cf->pool, 18 /* LIBINJECTION_XSS */);
|
|
if (!libjct_sql->sc_tag->data || !libjct_xss->sc_tag->data)
|
|
return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy((char*)libjct_sql->sc_tag->data, (char*)"$LIBINJECTION_SQL", 17);
|
|
memcpy((char*)libjct_xss->sc_tag->data, (char*)"$LIBINJECTION_XSS", 17);
|
|
libjct_xss->sc_tag->len = 17;
|
|
libjct_sql->sc_tag->len = 17;
|
|
libjct_sql->sc_score = 8;
|
|
libjct_xss->sc_score = 8;
|
|
|
|
return (NGX_OK);
|
|
}
|
|
|
|
/*
|
|
** my hugly configuration parsing function.
|
|
** should be rewritten, cause code is hugly and not bof proof at all
|
|
** does : top level parsing config function,
|
|
** see foo_cfg_parse.c for stuff
|
|
*/
|
|
static char*
|
|
ngx_http_naxsi_br_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t* alcf = conf;
|
|
|
|
ngx_str_t* value;
|
|
ngx_http_rule_t rule, *rule_r;
|
|
|
|
#ifdef _debug_readconf
|
|
if (cf) {
|
|
value = cf->args->elts;
|
|
NX_LOG_DEBUG(
|
|
_debug_readconf, NGX_LOG_EMERG, cf, 0, "TOP READ CONF %V %V", &(value[0]), &(value[1]));
|
|
}
|
|
#endif
|
|
if (!alcf || !cf) {
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
}
|
|
|
|
value = cf->args->elts;
|
|
|
|
/*
|
|
** if it's a basic rule
|
|
*/
|
|
if (!ngx_strcmp(value[0].data, TOP_BASIC_RULE_T) ||
|
|
!ngx_strcmp(value[0].data, TOP_BASIC_RULE_N)) {
|
|
memset(&rule, 0, sizeof(ngx_http_rule_t));
|
|
if (ngx_http_naxsi_cfg_parse_one_rule(cf, value, &rule, cf->args->nelts) != NGX_CONF_OK) {
|
|
/* LCOV_EXCL_START */
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return (NGX_CONF_ERROR);
|
|
/* LCOV_EXCL_STOP */
|
|
}
|
|
/* push in whitelist rules, as it have a whitelist ID array */
|
|
if (rule.wlid_array && rule.wlid_array->nelts > 0) {
|
|
if (alcf->whitelist_rules == NULL) {
|
|
alcf->whitelist_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->whitelist_rules == NULL) {
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
}
|
|
rule_r = ngx_array_push(alcf->whitelist_rules);
|
|
if (!rule_r) {
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
}
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
/* else push in appropriate ruleset : it's a normal rule */
|
|
else {
|
|
if (rule.br->headers || rule.br->headers_var) {
|
|
if (alcf->header_rules == NULL) {
|
|
alcf->header_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->header_rules == NULL)
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
rule_r = ngx_array_push(alcf->header_rules);
|
|
if (!rule_r)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
/* push in body match rules (PATCH/POST/PUT) */
|
|
if (rule.br->body || rule.br->body_var) {
|
|
if (alcf->body_rules == NULL) {
|
|
alcf->body_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->body_rules == NULL)
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
rule_r = ngx_array_push(alcf->body_rules);
|
|
if (!rule_r)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
/* push in raw body match rules (PATCH/POST/PUT) */
|
|
if (rule.br->raw_body) {
|
|
NX_LOG_DEBUG(_debug_readconf,
|
|
NGX_LOG_EMERG,
|
|
cf,
|
|
0,
|
|
"pushing rule %d in (read conf) raw_body rules",
|
|
rule.rule_id);
|
|
if (alcf->raw_body_rules == NULL) {
|
|
alcf->raw_body_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->raw_body_rules == NULL)
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
rule_r = ngx_array_push(alcf->raw_body_rules);
|
|
if (!rule_r)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
/* push in generic rules, as it's matching the URI */
|
|
if (rule.br->url) {
|
|
NX_LOG_DEBUG(
|
|
_debug_readconf, NGX_LOG_EMERG, cf, 0, "pushing rule %d in generic rules", rule.rule_id);
|
|
if (alcf->generic_rules == NULL) {
|
|
alcf->generic_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->generic_rules == NULL)
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
rule_r = ngx_array_push(alcf->generic_rules);
|
|
if (!rule_r)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
/* push in GET arg rules, but we should push in POST rules too */
|
|
if (rule.br->args_var || rule.br->args) {
|
|
NX_LOG_DEBUG(
|
|
_debug_readconf, NGX_LOG_EMERG, cf, 0, "pushing rule %d in GET rules", rule.rule_id);
|
|
if (alcf->get_rules == NULL) {
|
|
alcf->get_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->get_rules == NULL)
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
rule_r = ngx_array_push(alcf->get_rules);
|
|
if (!rule_r)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
}
|
|
|
|
if (ngx_http_naxsi_push_loc_conf(cf, alcf) != NGX_OK)
|
|
return NGX_CONF_ERROR;
|
|
|
|
return (NGX_CONF_OK);
|
|
}
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return (NGX_CONF_ERROR);
|
|
}
|
|
|
|
static char*
|
|
ngx_http_naxsi_cr_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
|
|
{
|
|
|
|
ngx_http_naxsi_loc_conf_t* alcf = conf;
|
|
ngx_str_t* value;
|
|
ngx_http_check_rule_t* rule_c;
|
|
unsigned int i;
|
|
u_char* var_end;
|
|
|
|
if (!alcf || !cf)
|
|
return (NGX_CONF_ERROR);
|
|
|
|
value = cf->args->elts;
|
|
|
|
if (ngx_strcmp(value[0].data, TOP_CHECK_RULE_T) && ngx_strcmp(value[0].data, TOP_CHECK_RULE_N))
|
|
return (NGX_CONF_ERROR);
|
|
|
|
/* #ifdef _debug_readconf */
|
|
/* ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, */
|
|
/* "pushing rule %d in check rules", rule.rule_id); */
|
|
/* #endif */
|
|
|
|
i = 0;
|
|
if (!alcf->check_rules)
|
|
alcf->check_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_check_rule_t));
|
|
if (!alcf->check_rules)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
rule_c = ngx_array_push(alcf->check_rules);
|
|
if (!rule_c)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memset(rule_c, 0, sizeof(ngx_http_check_rule_t));
|
|
/* process the first word : score rule */
|
|
if (value[1].data[i] == '$') {
|
|
var_end = (u_char*)ngx_strchr((value[1].data) + i, ' ');
|
|
if (!var_end) { /* LCOV_EXCL_START */
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return (NGX_CONF_ERROR);
|
|
/* LCOV_EXCL_STOP */
|
|
}
|
|
rule_c->sc_tag.len = var_end - value[1].data;
|
|
rule_c->sc_tag.data = ngx_pcalloc(cf->pool, rule_c->sc_tag.len + 1);
|
|
if (!rule_c->sc_tag.data)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_c->sc_tag.data, value[1].data, rule_c->sc_tag.len);
|
|
i += rule_c->sc_tag.len + 1;
|
|
} else {
|
|
/* LCOV_EXCL_START */
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return (NGX_CONF_ERROR);
|
|
/* LCOV_EXCL_STOP */
|
|
}
|
|
// move to next word
|
|
while (value[1].data[i] && value[1].data[i] == ' ')
|
|
i++;
|
|
// get the comparison type
|
|
if (value[1].data[i] == '>' && value[1].data[i + 1] == '=')
|
|
rule_c->cmp = SUP_OR_EQUAL;
|
|
else if (value[1].data[i] == '>' && value[1].data[i + 1] != '=')
|
|
rule_c->cmp = SUP;
|
|
else if (value[1].data[i] == '<' && value[1].data[i + 1] == '=')
|
|
rule_c->cmp = INF_OR_EQUAL;
|
|
else if (value[1].data[i] == '<' && value[1].data[i + 1] != '=')
|
|
rule_c->cmp = INF;
|
|
else {
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return (NGX_CONF_ERROR);
|
|
}
|
|
// move to next word
|
|
while (value[1].data[i] && !(value[1].data[i] >= '0' && value[1].data[i] <= '9') &&
|
|
(value[1].data[i] != '-'))
|
|
i++;
|
|
NX_LOG_DEBUG(_debug_readconf,
|
|
NGX_LOG_EMERG,
|
|
cf,
|
|
0,
|
|
"XX-special score in checkrule:%s from (%d)",
|
|
value[1].data,
|
|
atoi((const char*)value[1].data + i));
|
|
// get the score
|
|
rule_c->sc_score = atoi((const char*)(value[1].data + i));
|
|
/* process the second word : Action rule */
|
|
if (ngx_strstr(value[2].data, "BLOCK"))
|
|
rule_c->block = 1;
|
|
else if (ngx_strstr(value[2].data, "ALLOW"))
|
|
rule_c->allow = 1;
|
|
else if (ngx_strstr(value[2].data, "LOG"))
|
|
rule_c->log = 1;
|
|
else if (ngx_strstr(value[2].data, "DROP"))
|
|
rule_c->drop = 1;
|
|
else {
|
|
/* LCOV_EXCL_START */
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return (NGX_CONF_ERROR);
|
|
/* LCOV_EXCL_STOP */
|
|
}
|
|
|
|
if (ngx_http_naxsi_push_loc_conf(cf, alcf) != NGX_OK)
|
|
return NGX_CONF_ERROR;
|
|
|
|
return (NGX_CONF_OK);
|
|
}
|
|
|
|
/*
|
|
** URL denied
|
|
*/
|
|
static char*
|
|
ngx_http_naxsi_ud_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t* alcf = conf;
|
|
ngx_str_t* value;
|
|
|
|
if (!alcf || !cf)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
|
|
value = cf->args->elts;
|
|
|
|
/* store denied URL for location */
|
|
if ((!ngx_strcmp(value[0].data, TOP_DENIED_URL_N) ||
|
|
!ngx_strcmp(value[0].data, TOP_DENIED_URL_T)) &&
|
|
value[1].len) {
|
|
alcf->denied_url = ngx_pcalloc(cf->pool, sizeof(ngx_str_t));
|
|
if (!alcf->denied_url)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
alcf->denied_url->data = ngx_pcalloc(cf->pool, value[1].len + 1);
|
|
if (!alcf->denied_url->data)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(alcf->denied_url->data, value[1].data, value[1].len);
|
|
alcf->denied_url->len = value[1].len;
|
|
|
|
if (ngx_http_naxsi_push_loc_conf(cf, alcf) != NGX_OK)
|
|
return NGX_CONF_ERROR;
|
|
|
|
return (NGX_CONF_OK);
|
|
}
|
|
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
/* Add IP to Ignore IP list location configuration */
|
|
static ngx_int_t
|
|
ngx_http_naxsi_add_ii_loc_conf(ngx_conf_t* cf, ngx_http_naxsi_loc_conf_t* conf, char* ip_str)
|
|
{
|
|
ngx_str_t key;
|
|
|
|
key.len = strlen(ip_str);
|
|
key.data = (unsigned char*)ngx_pcalloc(cf->pool, key.len);
|
|
if (!key.data) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot allocate memory");
|
|
return (NGX_ERROR);
|
|
}
|
|
memcpy(key.data, ip_str, key.len);
|
|
|
|
if (!conf->ignore_ips) {
|
|
conf->ignore_ips = (ngx_hash_t*)ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
|
|
if (!conf->ignore_ips) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "IPs hashtable alloc failed");
|
|
return (NGX_ERROR);
|
|
}
|
|
conf->ignore_ips_ha.pool = cf->pool;
|
|
conf->ignore_ips_ha.temp_pool = cf->temp_pool;
|
|
if (ngx_hash_keys_array_init(&conf->ignore_ips_ha, NGX_HASH_SMALL) != NGX_OK) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "IPs hash keys init failed");
|
|
return (NGX_ERROR);
|
|
}
|
|
}
|
|
if (ngx_hash_add_key(&conf->ignore_ips_ha, &key, (void*)1234, NGX_HASH_READONLY_KEY) != NGX_OK) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot add hash value");
|
|
return (NGX_ERROR);
|
|
}
|
|
return (NGX_OK);
|
|
}
|
|
|
|
/*
|
|
** Ignore IP
|
|
*/
|
|
static char*
|
|
ngx_http_naxsi_ii_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t* alcf = conf;
|
|
ngx_str_t* value;
|
|
|
|
if (!alcf || !cf)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
|
|
value = cf->args->elts;
|
|
|
|
if (!ngx_strcmp(value[0].data, TOP_IGNORE_IP_T) || !ngx_strcmp(value[0].data, TOP_IGNORE_IP_N)) {
|
|
|
|
char ip_str[INET6_ADDRSTRLEN + 1] = { 0 };
|
|
|
|
if (!naxsi_parse_ip(&value[1], NULL, ip_str)) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid IP");
|
|
return (NGX_CONF_ERROR);
|
|
}
|
|
|
|
if (ngx_http_naxsi_add_ii_loc_conf(cf, alcf, ip_str) != NGX_OK)
|
|
return (NGX_CONF_ERROR);
|
|
|
|
if (ngx_http_naxsi_push_loc_conf(cf, alcf) != NGX_OK)
|
|
return NGX_CONF_ERROR;
|
|
|
|
return (NGX_CONF_OK);
|
|
}
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
/*
|
|
** Ignore CIDR
|
|
*/
|
|
static char*
|
|
ngx_http_naxsi_ic_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t* alcf = conf;
|
|
ngx_str_t* value;
|
|
|
|
if (!alcf || !cf)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
|
|
value = cf->args->elts;
|
|
|
|
if (!ngx_strcmp(value[0].data, TOP_IGNORE_CIDR_T) ||
|
|
!ngx_strcmp(value[0].data, TOP_IGNORE_CIDR_N)) {
|
|
|
|
char* smask = NULL;
|
|
int is_ipv6 = strnchr((const char*)value[1].data, ':', value[1].len) != NULL;
|
|
|
|
if ((!is_ipv6 && NULL != (smask = cstrfaststr(value[1].data, value[1].len, "/32"))) ||
|
|
(is_ipv6 && NULL != (smask = cstrfaststr(value[1].data, value[1].len, "/128")))) {
|
|
|
|
// add it directly to IgnoreIP list
|
|
char ip_str[INET6_ADDRSTRLEN + 1] = { 0 };
|
|
size_t orig_len = value[1].len;
|
|
|
|
value[1].len = smask - (const char*)value[1].data;
|
|
int ret = naxsi_parse_ip(&value[1], NULL, ip_str);
|
|
value[1].len = orig_len;
|
|
if (!ret) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid IP in CIDR");
|
|
return (NGX_CONF_ERROR);
|
|
}
|
|
|
|
if (ngx_http_naxsi_add_ii_loc_conf(cf, alcf, ip_str) != NGX_OK)
|
|
return (NGX_CONF_ERROR);
|
|
|
|
if (ngx_http_naxsi_push_loc_conf(cf, alcf) != NGX_OK)
|
|
return NGX_CONF_ERROR;
|
|
|
|
return (NGX_CONF_OK);
|
|
}
|
|
|
|
cidr_t cidr = cidr_zero;
|
|
int err = naxsi_parse_cidr(&value[1], &cidr);
|
|
switch (err) {
|
|
case CIDR_OK:
|
|
break;
|
|
default:
|
|
/* fall-thru */
|
|
case CIDR_ERROR_MISSING_MASK:
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "missing CIDR mask");
|
|
return (NGX_CONF_ERROR);
|
|
case CIDR_ERROR_INVALID_IP_NET:
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid CIDR net");
|
|
return (NGX_CONF_ERROR);
|
|
case CIDR_ERROR_INVALID_CIDR_MASK:
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid CIDR mask");
|
|
return (NGX_CONF_ERROR);
|
|
}
|
|
|
|
if (!alcf->ignore_cidrs) {
|
|
alcf->ignore_cidrs = ngx_array_create(cf->pool, 1, sizeof(cidr_t));
|
|
if (!alcf->ignore_cidrs) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "CIDRs array alloc failed");
|
|
return (NGX_CONF_ERROR);
|
|
}
|
|
}
|
|
|
|
cidr_t* tmp = (cidr_t*)ngx_array_push(alcf->ignore_cidrs);
|
|
if (!tmp) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot allocate array value");
|
|
return (NGX_CONF_ERROR);
|
|
}
|
|
*tmp = cidr;
|
|
|
|
if (ngx_http_naxsi_push_loc_conf(cf, alcf) != NGX_OK)
|
|
return NGX_CONF_ERROR;
|
|
|
|
return (NGX_CONF_OK);
|
|
}
|
|
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
/*
|
|
** handle flags that can be set/modified at runtime
|
|
*/
|
|
static char*
|
|
ngx_http_naxsi_flags_loc_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t* alcf = conf;
|
|
ngx_str_t* value;
|
|
|
|
if (!alcf || !cf)
|
|
return (NGX_CONF_ERROR);
|
|
|
|
if (ngx_http_naxsi_push_loc_conf(cf, alcf) != NGX_OK)
|
|
return NGX_CONF_ERROR;
|
|
|
|
value = cf->args->elts;
|
|
|
|
/* it's a flagrule, just a hack to enable/disable mod */
|
|
if (!ngx_strcmp(value[0].data, TOP_ENABLED_FLAG_T) ||
|
|
!ngx_strcmp(value[0].data, TOP_ENABLED_FLAG_N)) {
|
|
alcf->enabled = 1;
|
|
return (NGX_CONF_OK);
|
|
} else
|
|
/* it's a flagrule, just a hack to enable/disable mod */
|
|
if (!ngx_strcmp(value[0].data, TOP_DISABLED_FLAG_T) ||
|
|
!ngx_strcmp(value[0].data, TOP_DISABLED_FLAG_N)) {
|
|
alcf->force_disabled = 1;
|
|
return (NGX_CONF_OK);
|
|
} else
|
|
/* it's a flagrule, currently just a hack to enable/disable learning mode */
|
|
if (!ngx_strcmp(value[0].data, TOP_LEARNING_FLAG_T) ||
|
|
!ngx_strcmp(value[0].data, TOP_LEARNING_FLAG_N)) {
|
|
alcf->learning = 1;
|
|
return (NGX_CONF_OK);
|
|
} else if (!ngx_strcmp(value[0].data, TOP_LIBINJECTION_SQL_T) ||
|
|
!ngx_strcmp(value[0].data, TOP_LIBINJECTION_SQL_N)) {
|
|
NX_LOG_DEBUG(_debug_loc_conf, NGX_LOG_EMERG, cf, 0, "LibInjectionSql enabled");
|
|
alcf->libinjection_sql_enabled = 1;
|
|
return (NGX_CONF_OK);
|
|
} else if (!ngx_strcmp(value[0].data, TOP_LIBINJECTION_XSS_T) ||
|
|
!ngx_strcmp(value[0].data, TOP_LIBINJECTION_XSS_N)) {
|
|
alcf->libinjection_xss_enabled = 1;
|
|
NX_LOG_DEBUG(_debug_loc_conf, NGX_LOG_EMERG, cf, 0, "LibInjectionXss enabled");
|
|
return (NGX_CONF_OK);
|
|
} else
|
|
return (NGX_CONF_ERROR);
|
|
}
|
|
|
|
static char*
|
|
ngx_http_naxsi_read_main_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
|
|
{
|
|
ngx_http_naxsi_main_conf_t* alcf = conf;
|
|
ngx_str_t* value;
|
|
ngx_http_rule_t rule, *rule_r;
|
|
|
|
if (!alcf || !cf)
|
|
return (NGX_CONF_ERROR); /* alloc a new rule */
|
|
|
|
value = cf->args->elts;
|
|
/* parse the line, fill rule struct */
|
|
|
|
NX_LOG_DEBUG(_debug_main_conf, NGX_LOG_EMERG, cf, 0, "XX-TOP READ CONF %s", value[0].data);
|
|
if (ngx_strcmp(value[0].data, TOP_MAIN_BASIC_RULE_T) &&
|
|
ngx_strcmp(value[0].data, TOP_MAIN_BASIC_RULE_N)) {
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return (NGX_CONF_ERROR);
|
|
}
|
|
memset(&rule, 0, sizeof(ngx_http_rule_t));
|
|
|
|
if (ngx_http_naxsi_cfg_parse_one_rule(cf /*, alcf*/, value, &rule, cf->args->nelts) !=
|
|
NGX_CONF_OK) {
|
|
/* LCOV_EXCL_START */
|
|
ngx_http_naxsi_line_conf_error(cf, value);
|
|
return (NGX_CONF_ERROR);
|
|
/* LCOV_EXCL_STOP */
|
|
}
|
|
|
|
if (rule.br->headers || rule.br->headers_var) {
|
|
NX_LOG_DEBUG(
|
|
_debug_main_conf, NGX_LOG_EMERG, cf, 0, "pushing rule %d in header rules", rule.rule_id);
|
|
if (alcf->header_rules == NULL) {
|
|
alcf->header_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->header_rules == NULL)
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
rule_r = ngx_array_push(alcf->header_rules);
|
|
if (!rule_r)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
/* push in body match rules (PATCH/POST/PUT) */
|
|
if (rule.br->body || rule.br->body_var) {
|
|
NX_LOG_DEBUG(
|
|
_debug_main_conf, NGX_LOG_EMERG, cf, 0, "pushing rule %d in body rules", rule.rule_id);
|
|
if (alcf->body_rules == NULL) {
|
|
alcf->body_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->body_rules == NULL)
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
rule_r = ngx_array_push(alcf->body_rules);
|
|
if (!rule_r)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
/* push in raw body match rules (PATCH/POST/PUT) xx*/
|
|
if (rule.br->raw_body) {
|
|
NX_LOG_DEBUG(_debug_main_conf,
|
|
NGX_LOG_EMERG,
|
|
cf,
|
|
0,
|
|
"pushing rule %d in raw (main) body rules",
|
|
rule.rule_id);
|
|
if (alcf->raw_body_rules == NULL) {
|
|
alcf->raw_body_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->raw_body_rules == NULL)
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
rule_r = ngx_array_push(alcf->raw_body_rules);
|
|
if (!rule_r)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
/* push in generic rules, as it's matching the URI */
|
|
if (rule.br->url) {
|
|
NX_LOG_DEBUG(
|
|
_debug_main_conf, NGX_LOG_EMERG, cf, 0, "pushing rule %d in generic rules", rule.rule_id);
|
|
if (alcf->generic_rules == NULL) {
|
|
alcf->generic_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->generic_rules == NULL)
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
rule_r = ngx_array_push(alcf->generic_rules);
|
|
if (!rule_r)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
/* push in GET arg rules, but we should push in POST rules too */
|
|
if (rule.br->args_var || rule.br->args) {
|
|
NX_LOG_DEBUG(
|
|
_debug_main_conf, NGX_LOG_EMERG, cf, 0, "pushing rule %d in GET rules", rule.rule_id);
|
|
if (alcf->get_rules == NULL) {
|
|
alcf->get_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t));
|
|
if (alcf->get_rules == NULL)
|
|
return NGX_CONF_ERROR; /* LCOV_EXCL_LINE */
|
|
}
|
|
rule_r = ngx_array_push(alcf->get_rules);
|
|
if (!rule_r)
|
|
return (NGX_CONF_ERROR); /* LCOV_EXCL_LINE */
|
|
memcpy(rule_r, &rule, sizeof(ngx_http_rule_t));
|
|
}
|
|
return (NGX_CONF_OK);
|
|
}
|
|
|
|
/*
|
|
** [ENTRY POINT] does : this is the function called by nginx :
|
|
** - Set up the context for the request
|
|
** - Check if the job is done and we're called again
|
|
** - if it's a PATCH/POST/PUT request, setup hook for body dataz
|
|
** - call dummy_data_parse
|
|
** - check our context struct (with scores & stuff) against custom check rules
|
|
** - check if the request should be denied
|
|
*/
|
|
static ngx_int_t
|
|
ngx_http_naxsi_access_handler(ngx_http_request_t* r)
|
|
{
|
|
ngx_http_request_ctx_t* ctx;
|
|
ngx_int_t rc;
|
|
ngx_http_naxsi_loc_conf_t* cf;
|
|
struct tms tmsstart, tmsend;
|
|
clock_t start, end;
|
|
ngx_http_variable_value_t* lookup;
|
|
|
|
static const ngx_str_t learning_flag = ngx_string(RT_LEARNING);
|
|
static const ngx_str_t enable_flag = ngx_string(RT_ENABLE);
|
|
static const ngx_str_t post_action_flag = ngx_string(RT_POST_ACTION);
|
|
static const ngx_str_t extensive_log_flag = ngx_string(RT_EXTENSIVE_LOG);
|
|
static const ngx_str_t json_log_flag = ngx_string(RT_JSON_LOG);
|
|
static const ngx_str_t libinjection_sql_flag = ngx_string(RT_LIBINJECTION_SQL);
|
|
static const ngx_str_t libinjection_xss_flag = ngx_string(RT_LIBINJECTION_XSS);
|
|
|
|
ctx = ngx_http_get_module_ctx(r, ngx_http_naxsi_module);
|
|
cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module);
|
|
|
|
if (ctx && ctx->over)
|
|
return (NGX_DECLINED);
|
|
if (ctx && ctx->wait_for_body) {
|
|
NX_DEBUG(_debug_mechanics, NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "naxsi:NGX_AGAIN");
|
|
return (NGX_DONE);
|
|
}
|
|
if (!cf)
|
|
return (NGX_ERROR);
|
|
/* the module is not enabled here */
|
|
/* if enable directive is not present at all in the location,
|
|
don't try to do dynamic lookup for "live" enabled
|
|
naxsi, this would be very rude. */
|
|
if (!cf->enabled)
|
|
return (NGX_DECLINED);
|
|
/* On the other hand, if naxsi has been explicitly disabled
|
|
in this location (using naxsi directive), user is probably
|
|
trying to do something. */
|
|
if (cf->force_disabled) {
|
|
/* Look if the user did not try to enable naxsi dynamically */
|
|
lookup = ngx_http_get_variable(r, (ngx_str_t*)&enable_flag, cf->flag_enable_h);
|
|
if (lookup && !lookup->not_found && lookup->len > 0) {
|
|
ngx_log_debug(NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"live enable is present %d",
|
|
lookup->data[0] - '0');
|
|
if (lookup->data[0] - '0' != 1) {
|
|
return (NGX_DECLINED);
|
|
}
|
|
} else
|
|
return (NGX_DECLINED);
|
|
}
|
|
/* don't process internal requests. */
|
|
if (r->internal) {
|
|
NX_DEBUG(_debug_mechanics,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-DON'T PROCESS (%V)|CTX:%p|ARGS:%V|METHOD=%s|INTERNAL:%d",
|
|
&(r->uri),
|
|
ctx,
|
|
&(r->args),
|
|
r->method == NGX_HTTP_PATCH ? "PATCH"
|
|
: r->method == NGX_HTTP_POST ? "POST"
|
|
: r->method == NGX_HTTP_PUT ? "PUT"
|
|
: r->method == NGX_HTTP_GET ? "GET"
|
|
: "UNKNOWN!!",
|
|
r->internal);
|
|
return (NGX_DECLINED);
|
|
}
|
|
NX_DEBUG(_debug_mechanics,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-processing (%V)|CTX:%p|ARGS:%V|METHOD=%s|INTERNAL:%d",
|
|
&(r->uri),
|
|
ctx,
|
|
&(r->args),
|
|
r->method == NGX_HTTP_PATCH ? "PATCH"
|
|
: r->method == NGX_HTTP_POST ? "POST"
|
|
: r->method == NGX_HTTP_PUT ? "PUT"
|
|
: r->method == NGX_HTTP_GET ? "GET"
|
|
: "UNKNOWN!!",
|
|
r->internal);
|
|
if (!ctx) {
|
|
ngx_pool_cleanup_t* cln;
|
|
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_request_ctx_t));
|
|
if (ctx == NULL)
|
|
return NGX_ERROR;
|
|
cln = ngx_pool_cleanup_add(r->pool, 0);
|
|
if (cln == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
cln->handler = ngx_http_module_cleanup_handler;
|
|
cln->data = ctx;
|
|
|
|
ngx_http_set_ctx(r, ctx, ngx_http_naxsi_module);
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : orig learning : %d",
|
|
cf->learning ? 1 : 0);
|
|
/* it seems that nginx will - in some cases -
|
|
have a variable with empty content but with lookup->not_found set to 0,
|
|
so check len as well */
|
|
ctx->learning = cf->learning;
|
|
|
|
lookup = ngx_http_get_variable(r, (ngx_str_t*)&learning_flag, cf->flag_learning_h);
|
|
if (lookup && !lookup->not_found && lookup->len > 0) {
|
|
|
|
ctx->learning = lookup->data[0] - '0';
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : override learning : %d (raw=%d)",
|
|
ctx->learning ? 1 : 0,
|
|
lookup->len);
|
|
}
|
|
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : [final] learning : %d",
|
|
ctx->learning ? 1 : 0);
|
|
|
|
ctx->enabled = cf->enabled;
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : orig enabled : %d",
|
|
ctx->enabled ? 1 : 0);
|
|
|
|
lookup = ngx_http_get_variable(r, (ngx_str_t*)&enable_flag, cf->flag_enable_h);
|
|
if (lookup && !lookup->not_found && lookup->len > 0) {
|
|
ctx->enabled = lookup->data[0] - '0';
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : override enable : %d",
|
|
ctx->enabled ? 1 : 0);
|
|
}
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : [final] enabled : %d",
|
|
ctx->enabled ? 1 : 0);
|
|
|
|
/*
|
|
** LIBINJECTION_SQL
|
|
*/
|
|
ctx->libinjection_sql = cf->libinjection_sql_enabled;
|
|
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : orig libinjection_sql : %d",
|
|
ctx->libinjection_sql ? 1 : 0);
|
|
|
|
lookup =
|
|
ngx_http_get_variable(r, (ngx_str_t*)&libinjection_sql_flag, cf->flag_libinjection_sql_h);
|
|
if (lookup && !lookup->not_found && lookup->len > 0) {
|
|
ctx->libinjection_sql = lookup->data[0] - '0';
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : override libinjection_sql : %d",
|
|
ctx->libinjection_sql ? 1 : 0);
|
|
}
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : [final] libinjection_sql : %d",
|
|
ctx->libinjection_sql ? 1 : 0);
|
|
|
|
/*
|
|
** LIBINJECTION_XSS
|
|
*/
|
|
ctx->libinjection_xss = cf->libinjection_xss_enabled;
|
|
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : orig libinjection_xss : %d",
|
|
ctx->libinjection_xss ? 1 : 0);
|
|
|
|
lookup =
|
|
ngx_http_get_variable(r, (ngx_str_t*)&libinjection_xss_flag, cf->flag_libinjection_xss_h);
|
|
if (lookup && !lookup->not_found && lookup->len > 0) {
|
|
ctx->libinjection_xss = lookup->data[0] - '0';
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : override libinjection_xss : %d",
|
|
ctx->libinjection_xss ? 1 : 0);
|
|
}
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : [final] libinjection_xss : %d",
|
|
ctx->libinjection_xss ? 1 : 0);
|
|
|
|
/* post_action is off by default. */
|
|
ctx->post_action = 0;
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : orig post_action : %d",
|
|
ctx->post_action ? 1 : 0);
|
|
|
|
lookup = ngx_http_get_variable(r, (ngx_str_t*)&post_action_flag, cf->flag_post_action_h);
|
|
if (lookup && !lookup->not_found && lookup->len > 0) {
|
|
ctx->post_action = lookup->data[0] - '0';
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : override post_action : %d",
|
|
ctx->post_action ? 1 : 0);
|
|
}
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : [final] post_action : %d",
|
|
ctx->post_action ? 1 : 0);
|
|
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : orig extensive_log : %d",
|
|
ctx->extensive_log ? 1 : 0);
|
|
|
|
lookup = ngx_http_get_variable(r, (ngx_str_t*)&extensive_log_flag, cf->flag_extensive_log_h);
|
|
if (lookup && !lookup->not_found && lookup->len > 0) {
|
|
ctx->extensive_log = lookup->data[0] - '0';
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : override extensive_log : %d",
|
|
ctx->extensive_log ? 1 : 0);
|
|
}
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : [final] extensive_log : %d",
|
|
ctx->extensive_log ? 1 : 0);
|
|
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : orig json_log : %d",
|
|
ctx->json_log ? 1 : 0);
|
|
|
|
lookup = ngx_http_get_variable(r, (ngx_str_t*)&json_log_flag, cf->flag_json_log_h);
|
|
if (lookup && !lookup->not_found && lookup->len > 0) {
|
|
ctx->json_log = lookup->data[0] - '0';
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : override json_log : %d",
|
|
ctx->json_log ? 1 : 0);
|
|
}
|
|
NX_DEBUG(_debug_modifier,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : [final] json_log : %d",
|
|
ctx->json_log ? 1 : 0);
|
|
|
|
/* the module is not enabled here */
|
|
if (!ctx->enabled)
|
|
return (NGX_DECLINED);
|
|
|
|
if ((r->method == NGX_HTTP_PATCH || r->method == NGX_HTTP_POST || r->method == NGX_HTTP_PUT) &&
|
|
!ctx->ready) {
|
|
NX_DEBUG(_debug_mechanics,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : body_request : before !");
|
|
|
|
rc = ngx_http_read_client_request_body(r, ngx_http_naxsi_payload_handler);
|
|
/* this might happen quite often, especially with big files /
|
|
** low network speed. our handler is called when headers are read,
|
|
** but, often, the full body request hasn't yet, so
|
|
** read client request body will return ngx_again. Then we need
|
|
** to return ngx_done, wait for our handler to be called once
|
|
** body request arrived, and let him call core_run_phases
|
|
** to be able to process the request.
|
|
*/
|
|
if (rc == NGX_AGAIN) {
|
|
ctx->wait_for_body = 1;
|
|
NX_DEBUG(_debug_mechanics,
|
|
NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"XX-dummy : body_request : NGX_AGAIN !");
|
|
|
|
return (NGX_DONE);
|
|
} else if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
|
/*
|
|
** might happen but never saw it, let the debug print.
|
|
*/
|
|
ngx_log_debug(
|
|
NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : SPECIAL RESPONSE !!!!");
|
|
return rc;
|
|
}
|
|
} else
|
|
ctx->ready = 1;
|
|
}
|
|
if (ctx && ctx->ready && !ctx->over) {
|
|
|
|
if ((start = times(&tmsstart)) == (clock_t)-1) {
|
|
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : Failed to get time");
|
|
}
|
|
|
|
ngx_http_naxsi_data_parse(ctx, r);
|
|
cf->request_processed++;
|
|
if ((end = times(&tmsend)) == (clock_t)-1) {
|
|
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : Failed to get time");
|
|
}
|
|
|
|
if (end - start > 10) {
|
|
ngx_log_debug(NGX_LOG_DEBUG_HTTP,
|
|
r->connection->log,
|
|
0,
|
|
"[MORE THAN 10MS] times : start:%l end:%l diff:%l",
|
|
start,
|
|
end,
|
|
(end - start));
|
|
}
|
|
|
|
ctx->over = 1;
|
|
if (ctx->block || ctx->drop) {
|
|
cf->request_blocked++;
|
|
rc = ngx_http_output_forbidden_page(ctx, r);
|
|
// nothing: return (NGX_OK);
|
|
// redirect: return (NGX_HTTP_OK);
|
|
return rc;
|
|
} else if (ctx->log) {
|
|
rc = ngx_http_output_forbidden_page(ctx, r);
|
|
}
|
|
}
|
|
NX_DEBUG(_debug_mechanics, NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "NGX_FINISHED !");
|
|
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
static ngx_http_request_ctx_t*
|
|
recover_request_ctx(ngx_http_request_t* r)
|
|
{
|
|
ngx_http_request_ctx_t* ctx;
|
|
ngx_pool_cleanup_t* cln;
|
|
ctx = ngx_http_get_module_ctx(r, ngx_http_naxsi_module);
|
|
if (ctx == NULL && (r->internal || r->filter_finalize)) {
|
|
for (cln = r->pool->cleanup; cln; cln = cln->next) {
|
|
if (cln->handler == ngx_http_module_cleanup_handler) {
|
|
ctx = cln->data;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
static void
|
|
ngx_http_module_cleanup_handler(void* data)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_add_variables(ngx_conf_t* cf)
|
|
{
|
|
ngx_http_variable_t *var, *v;
|
|
|
|
for (v = ngx_http_naxsi_variables; v->name.len; v++) {
|
|
var = ngx_http_add_variable(cf, &v->name, v->flags);
|
|
if (var == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
var->get_handler = v->get_handler;
|
|
var->data = v->data;
|
|
}
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_server_variable(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data)
|
|
{
|
|
v->data = r->headers_in.server.data;
|
|
v->len = r->headers_in.server.len;
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_uri_variable(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data)
|
|
{
|
|
ngx_http_request_ctx_t* ctx = recover_request_ctx(r);
|
|
if (!ctx) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
ngx_str_t* tmp_uri = ngx_pcalloc(r->pool, sizeof(ngx_str_t));
|
|
if (!tmp_uri) {
|
|
return (NGX_ERROR);
|
|
}
|
|
tmp_uri->len = r->uri.len + (2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, NGX_ESCAPE_ARGS));
|
|
tmp_uri->data = ngx_pcalloc(r->pool, tmp_uri->len + 1);
|
|
if (!tmp_uri->data) {
|
|
return (NGX_ERROR);
|
|
}
|
|
ngx_escape_uri(tmp_uri->data, r->uri.data, r->uri.len, NGX_ESCAPE_ARGS);
|
|
|
|
v->data = tmp_uri->data;
|
|
v->len = tmp_uri->len;
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_learning_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data)
|
|
{
|
|
ngx_http_request_ctx_t* ctx = recover_request_ctx(r);
|
|
if (!ctx) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
v->data = (u_char*)(ctx->learning ? "1" : "0");
|
|
v->len = 1;
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_block_variable(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data)
|
|
{
|
|
ngx_http_request_ctx_t* ctx = recover_request_ctx(r);
|
|
if (!ctx) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
v->data = (u_char*)(ctx->block ? "1" : "0");
|
|
v->len = 1;
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_total_processed_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t* cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module);
|
|
|
|
v->data = ngx_palloc(r->pool, NGX_INT32_LEN);
|
|
if (v->data == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
v->len = ngx_sprintf(v->data, "%z", cf->request_processed) - v->data;
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_total_blocked_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data)
|
|
{
|
|
ngx_http_naxsi_loc_conf_t* cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module);
|
|
|
|
v->data = ngx_palloc(r->pool, NGX_INT32_LEN);
|
|
if (v->data == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
v->len = ngx_sprintf(v->data, "%z", cf->request_blocked) - v->data;
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_score_variable(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data)
|
|
{
|
|
ngx_http_request_ctx_t* ctx = recover_request_ctx(r);
|
|
if (!ctx) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
const char* fmt = "%s:%d,"; /* cscore:score */
|
|
ngx_http_matched_rule_t* mr = NULL;
|
|
ngx_http_special_score_t* sc = NULL;
|
|
ngx_uint_t others = 0, i = 0;
|
|
size_t size = 0, written = 0;
|
|
char* p = NULL;
|
|
|
|
if (ctx->matched) {
|
|
mr = ctx->matched->elts;
|
|
for (i = 0; i < ctx->matched->nelts; i++) {
|
|
if (mr[i].rule->rule_id < 1000) {
|
|
others = 1;
|
|
size += strlen("$INTERNAL,");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ctx->special_scores) {
|
|
sc = ctx->special_scores->elts;
|
|
for (i = 0; i < ctx->special_scores->nelts; i++) {
|
|
if (sc[i].sc_score == 0) {
|
|
continue;
|
|
}
|
|
size += snprintf(NULL, 0, fmt, sc[i].sc_tag->data, sc[i].sc_score);
|
|
}
|
|
}
|
|
|
|
if (size < 1) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
v->len = size - 1; /* - last ',' */
|
|
v->data = ngx_palloc(r->pool, size);
|
|
if (v->data == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
p = (char*)v->data;
|
|
|
|
if (others) {
|
|
written = strlen("$INTERNAL,");
|
|
memcpy(p, "$INTERNAL,", written + 1);
|
|
}
|
|
|
|
if (ctx->special_scores) {
|
|
sc = ctx->special_scores->elts;
|
|
for (i = 0; i < ctx->special_scores->nelts; i++) {
|
|
if (sc[i].sc_score == 0) {
|
|
continue;
|
|
}
|
|
|
|
int sub = snprintf(p + written, size - written, fmt, sc[i].sc_tag->data, sc[i].sc_score);
|
|
if (sub < 0) {
|
|
break;
|
|
}
|
|
written += sub;
|
|
}
|
|
}
|
|
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_match_variable(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data)
|
|
{
|
|
ngx_http_request_ctx_t* ctx = recover_request_ctx(r);
|
|
if (!ctx) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
const char* fmt = "%d:%s%s:%s,"; /* rule_id:zone:var_name */
|
|
ngx_http_matched_rule_t* mr = NULL;
|
|
ngx_uint_t i = 0;
|
|
size_t size = 0;
|
|
size_t written = 0;
|
|
char* p = NULL;
|
|
|
|
if (ctx->matched) {
|
|
mr = ctx->matched->elts;
|
|
for (i = 0; i < ctx->matched->nelts; i++) {
|
|
const char* var_name = mr[i].name->len ? (const char*)mr[i].name->data : "-";
|
|
ngx_uint_t rule_id = mr[i].rule->rule_id;
|
|
/* FILE_EXT|NAME is the longest zone we may have */
|
|
size += snprintf(NULL, 0, fmt, rule_id, "FILE_EXT", "|NAME", var_name);
|
|
}
|
|
}
|
|
|
|
if (size < 1) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
v->data = ngx_palloc(r->pool, size);
|
|
if (v->data == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
p = (char*)v->data;
|
|
mr = ctx->matched->elts;
|
|
for (i = 0; i < ctx->matched->nelts; i++) {
|
|
const char* var_name = mr[i].name->len ? (const char*)mr[i].name->data : "-";
|
|
const char* zone = NULL;
|
|
const char* name = mr[i].target_name ? "|NAME" : "";
|
|
ngx_uint_t rule_id = mr[i].rule->rule_id;
|
|
|
|
if (mr[i].body_var) {
|
|
zone = "BODY";
|
|
} else if (mr[i].args_var) {
|
|
zone = "ARGS";
|
|
} else if (mr[i].headers_var) {
|
|
zone = "HEADERS";
|
|
} else if (mr[i].url) {
|
|
zone = "URL";
|
|
} else if (mr[i].file_ext) {
|
|
zone = "FILE_EXT";
|
|
} else {
|
|
/* should never happen.. */
|
|
continue;
|
|
}
|
|
|
|
int sub = snprintf(p + written, size - written, fmt, rule_id, zone, name, var_name);
|
|
if (sub < 0) {
|
|
break;
|
|
}
|
|
written += sub;
|
|
}
|
|
|
|
v->len = written > 0 ? written - 1 : 0; /* - last ',' */
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_attack_family_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data)
|
|
{
|
|
ngx_http_request_ctx_t* ctx = recover_request_ctx(r);
|
|
if (!ctx) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
ngx_http_special_score_t* sc;
|
|
ngx_http_matched_rule_t* mr;
|
|
ngx_uint_t i;
|
|
size_t sz = 0;
|
|
u_char* str;
|
|
u_char* p;
|
|
|
|
ngx_uint_t others = 0;
|
|
if (ctx->matched) {
|
|
mr = ctx->matched->elts;
|
|
for (i = 0; i < ctx->matched->nelts; i++) {
|
|
if (mr[i].rule->rule_id < 1000) {
|
|
others = 1;
|
|
sz = strlen("$INTERNAL,");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ctx->special_scores) {
|
|
sc = ctx->special_scores->elts;
|
|
for (i = 0; i < ctx->special_scores->nelts; i++) {
|
|
if (sc[i].sc_score != 0) {
|
|
sz += sc[i].sc_tag->len + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sz < 1) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
str = (u_char*)ngx_pcalloc(r->pool, sz);
|
|
if (str == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
p = str;
|
|
|
|
if (others) {
|
|
memcpy(p, "$INTERNAL,", sizeof("$INTERNAL,"));
|
|
p += strlen("$INTERNAL,");
|
|
}
|
|
|
|
if (ctx->special_scores) {
|
|
sc = ctx->special_scores->elts;
|
|
for (i = 0; i < ctx->special_scores->nelts; i++) {
|
|
if (sc[i].sc_score != 0) {
|
|
memcpy(p, sc[i].sc_tag->data, sc[i].sc_tag->len);
|
|
p += sc[i].sc_tag->len;
|
|
*p = ',';
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
|
|
v->data = str;
|
|
v->len = sz - 1;
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_attack_action_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data)
|
|
{
|
|
ngx_http_request_ctx_t* ctx = recover_request_ctx(r);
|
|
if (!ctx) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
size_t sz = 0;
|
|
u_char* str = NULL;
|
|
const char* variable = NULL;
|
|
// least significant bit represents if action is pass or block
|
|
// second least significant bit represents if naxsi is in learning mode
|
|
u_int learning_block_bits = 0;
|
|
|
|
learning_block_bits = ((ctx->learning ? 1 : 0) << 1) | (ctx->block ? 1 : 0);
|
|
|
|
switch (learning_block_bits) {
|
|
case 0: // pass
|
|
variable = "$PASS";
|
|
break;
|
|
case 1: // block
|
|
variable = "$BLOCK";
|
|
break;
|
|
case 2: // learning pass
|
|
variable = "$LEARNING-PASS";
|
|
break;
|
|
case 3: // learning block
|
|
variable = "$LEARNING-BLOCK";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!variable) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
sz = strlen(variable);
|
|
str = (u_char*)ngx_pcalloc(r->pool, sz);
|
|
if (str == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
memcpy(str, variable, sz);
|
|
|
|
v->data = str;
|
|
v->len = sz;
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_http_naxsi_request_id_variable(ngx_http_request_t* r,
|
|
ngx_http_variable_value_t* v,
|
|
uintptr_t data)
|
|
{
|
|
u_char* req_id = naxsi_request_id(r);
|
|
|
|
if (req_id[0] == 0) {
|
|
v->not_found = 1;
|
|
return NGX_OK;
|
|
}
|
|
|
|
v->valid = 1;
|
|
v->no_cacheable = 0;
|
|
v->not_found = 0;
|
|
v->len = NAXSI_REQUEST_ID_STRLEN;
|
|
v->data = req_id;
|
|
return NGX_OK;
|
|
}
|
|
|
|
u_char*
|
|
naxsi_request_id(ngx_http_request_t* req)
|
|
{
|
|
ngx_http_request_ctx_t* ctx = recover_request_ctx(req);
|
|
if (!ctx)
|
|
return (u_char*)"";
|
|
|
|
if (ctx->request_id[0] != 0)
|
|
return ctx->request_id;
|
|
|
|
static const ngx_str_t request_id_varname = ngx_string(RT_REQUEST_ID);
|
|
ngx_http_variable_value_t* lookup;
|
|
|
|
lookup = ngx_http_get_variable(req, (ngx_str_t*)&request_id_varname, request_id_h);
|
|
if (lookup && !lookup->not_found && lookup->len > 0) {
|
|
memcpy(ctx->request_id, lookup->data, NAXSI_REQUEST_ID_STRLEN);
|
|
return ctx->request_id;
|
|
}
|
|
|
|
/* NGINX request_id variable does not defined */
|
|
u_char bytes[NAXSI_REQUEST_ID_SIZE];
|
|
#if (NGX_OPENSSL)
|
|
if (RAND_bytes(bytes, NAXSI_REQUEST_ID_SIZE) == 1) {
|
|
ngx_hex_dump(ctx->request_id, bytes, NAXSI_REQUEST_ID_SIZE);
|
|
return ctx->request_id;
|
|
}
|
|
#else
|
|
uint32_t* bytes32 = (uint32_t*)bytes;
|
|
const size_t len = (NAXSI_REQUEST_ID_SIZE / sizeof(uint32_t));
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
bytes32[i] = (uint32_t)ngx_random();
|
|
}
|
|
#endif
|
|
ngx_hex_dump(ctx->request_id, bytes, NAXSI_REQUEST_ID_SIZE);
|
|
return ctx->request_id;
|
|
}
|