Include data edition, sanitizematched and few fixes

This commit is contained in:
brenosilva 2011-02-14 12:49:55 +00:00
parent 37e8cba181
commit 7f52d86e4b
13 changed files with 762 additions and 75 deletions

View File

@ -104,6 +104,10 @@ void *create_directory_config(apr_pool_t *mp, char *path)
/* Content injection. */
dcfg->content_injection_enabled = NOT_SET;
/* Stream inspection */
dcfg->stream_inbody_inspection = NOT_SET;
dcfg->stream_outbody_inspection = NOT_SET;
/* Geo Lookups */
dcfg->geo = NOT_SET_P;
@ -445,6 +449,12 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child)
merged->content_injection_enabled = (child->content_injection_enabled == NOT_SET
? parent->content_injection_enabled : child->content_injection_enabled);
/* Stream inspection */
merged->stream_inbody_inspection = (child->stream_inbody_inspection == NOT_SET
? parent->stream_inbody_inspection : child->stream_inbody_inspection);
merged->stream_outbody_inspection = (child->stream_outbody_inspection == NOT_SET
? parent->stream_outbody_inspection : child->stream_outbody_inspection);
/* Geo Lookup */
merged->geo = (child->geo == NOT_SET_P
? parent->geo : child->geo);
@ -543,6 +553,10 @@ void init_directory_config(directory_config *dcfg)
/* Content injection. */
if (dcfg->content_injection_enabled == NOT_SET) dcfg->content_injection_enabled = 0;
/* Stream inspection */
if (dcfg->stream_inbody_inspection == NOT_SET) dcfg->stream_inbody_inspection = 0;
if (dcfg->stream_outbody_inspection == NOT_SET) dcfg->stream_outbody_inspection = 0;
/* Geo Lookup */
if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL;
@ -1334,6 +1348,72 @@ static const char *cmd_guardian_log(cmd_parms *cmd, void *_dcfg,
return NULL;
}
/*
* \brief Add StreamInBodyInspection configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
static const char *cmd_stream_inbody_inspection(cmd_parms *cmd, void *_dcfg, int flag)
{
directory_config *dcfg = (directory_config *)_dcfg;
if (dcfg == NULL) return NULL;
dcfg->stream_inbody_inspection = flag;
return NULL;
}
/*
* \brief Add StreamOutBodyInspection configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
static const char *cmd_stream_outbody_inspection(cmd_parms *cmd, void *_dcfg, int flag)
{
directory_config *dcfg = (directory_config *)_dcfg;
if (dcfg == NULL) return NULL;
dcfg->stream_outbody_inspection = flag;
return NULL;
}
/*
* \brief Add SecReadStateLimit configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg,
const char *p1)
{
directory_config *dcfg = (directory_config *)_dcfg;
long int limit;
if (dcfg == NULL) return NULL;
limit = strtol(p1, NULL, 10);
if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecReadStateLimit: %s", p1);
}
conn_read_state_limit = limit;
return NULL;
}
static const char *cmd_request_body_inmemory_limit(cmd_parms *cmd, void *_dcfg,
const char *p1)
{
@ -2029,6 +2109,22 @@ const command_rec module_directives[] = {
"On or Off"
),
AP_INIT_FLAG (
"StreamOutBodyInspection",
cmd_stream_outbody_inspection,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_FLAG (
"StreamInBodyInspection",
cmd_stream_inbody_inspection,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE1 (
"SecCookieFormat",
cmd_cookie_format,
@ -2134,6 +2230,14 @@ const command_rec module_directives[] = {
"On or Off"
),
AP_INIT_TAKE1 (
"SecReadStateLimit",
cmd_conn_read_state_limit,
NULL,
CMD_SCOPE_ANY,
"maximum number of threads in READ_BUSY state per ip address"
),
AP_INIT_TAKE1 (
"SecRequestBodyInMemoryLimit",
cmd_request_body_inmemory_limit,

View File

@ -21,7 +21,6 @@
#include "modsecurity.h"
#include "apache2.h"
/* -- Input filter -- */
#if 0
@ -89,7 +88,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out,
return APR_EGENERAL;
}
if (chunk) {
if (chunk && !msr->txcfg->stream_inbody_inspection) {
/* Copy the data we received in the chunk */
bucket = apr_bucket_heap_create(chunk->data, chunk->length, NULL,
f->r->connection->bucket_alloc);
@ -117,6 +116,22 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out,
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Input filter: Forwarded %" APR_SIZE_T_FMT " bytes.", chunk->length);
}
} else if (msr->stream_input_data != NULL) {
bucket = apr_bucket_heap_create(msr->stream_input_data, msr->stream_input_length, NULL,
f->r->connection->bucket_alloc);
if (bucket == NULL) return APR_EGENERAL;
APR_BRIGADE_INSERT_TAIL(bb_out, bucket);
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Input stream filter: Forwarded %" APR_SIZE_T_FMT " bytes.", msr->msc_reqbody_disk_chunk->length);
}
}
if(msr->txcfg->stream_inbody_inspection && msr->stream_input_data != NULL) {
free(msr->stream_input_data);
msr->stream_input_data = NULL;
}
if (rc == 0) {
@ -259,7 +274,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Input filter: Completed receiving request body (length %" APR_SIZE_T_FMT ").",
msr->reqbody_length);
msr->reqbody_length);
}
msr->if_status = IF_STATUS_WANTS_TO_RUN;
@ -409,9 +424,9 @@ static apr_status_t send_of_brigade(modsec_rec *msr, ap_filter_t *f) {
rc = ap_pass_brigade(f->next, msr->of_brigade);
if (rc != APR_SUCCESS) {
/* TODO: These need to move to flags in 2.6. For now log them
* at level 3 so that they are not confusing users.
* at level 4 so that they are not confusing users.
*/
int log_level = 3;
int log_level = 4;
if (msr->txcfg->debuglog_level >= log_level) {
switch(rc) {
@ -438,6 +453,34 @@ static apr_status_t send_of_brigade(modsec_rec *msr, ap_filter_t *f) {
return APR_SUCCESS;
}
static void inject_content_to_of_brigade(modsec_rec *msr, ap_filter_t *f) {
apr_bucket *b;
if (msr->txcfg->content_injection_enabled && msr->stream_output_data != NULL) {
apr_bucket *bucket_ci = NULL;
apr_bucket *bucket_eos = NULL;
bucket_ci = apr_bucket_heap_create(msr->stream_output_data,
msr->stream_output_length, NULL, f->r->connection->bucket_alloc);
for (b = APR_BRIGADE_FIRST(msr->of_brigade); b != APR_BRIGADE_SENTINEL(msr->of_brigade); b = APR_BUCKET_NEXT(b)) {
if(!APR_BUCKET_IS_METADATA(b))
apr_bucket_delete(b);
}
APR_BRIGADE_INSERT_HEAD(msr->of_brigade, bucket_ci);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Content Injection: Data reinjected bytes [%d]",msr->stream_output_length);
}
}
if(msr->stream_output_data != NULL) {
free(msr->stream_output_data);
msr->stream_output_data = NULL;
}
}
/**
*
*/
@ -486,6 +529,19 @@ static int flatten_response_body(modsec_rec *msr) {
msr->resbody_data[msr->resbody_length] = '\0';
msr->resbody_status = RESBODY_STATUS_READ;
if (msr->txcfg->stream_outbody_inspection) {
msr->stream_output_data = (char *)malloc(msr->resbody_length+1);
msr->stream_output_length = msr->resbody_length+1;
if (msr->stream_output_data == NULL) {
msr_log(msr, 1, "Output filter: Stream Response body data memory allocation failed. Asked for: %" APR_SIZE_T_FMT,
msr->stream_output_length + 1);
return -1;
}
apr_cpystrn(msr->stream_output_data,msr->resbody_data,msr->stream_output_length);
}
return 1;
}
@ -801,6 +857,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) {
* (full-buffering only).
*/
if ((msr->of_skipping == 0)&&(!msr->of_partial)) {
inject_content_to_of_brigade(msr,f);
prepend_content_to_of_brigade(msr, f);
/* Inject content into response (append & buffering). */

View File

@ -460,12 +460,12 @@ static apr_status_t module_cleanup(void *data) {
*/
static const char *modsec_var_log_handler(request_rec *r, char *name) {
modsec_rec *msr = NULL;
if (name == NULL) return NULL;
msr = retrieve_tx_context(r);
if (msr == NULL) return NULL;
return construct_single_var(msr, name);
}
@ -474,7 +474,7 @@ static const char *modsec_var_log_handler(request_rec *r, char *name) {
*/
static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp) {
static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
/* Initialise ModSecurity engine */
modsecurity = modsecurity_create(mp, MODSEC_ONLINE);
if (modsecurity == NULL) {
@ -482,7 +482,7 @@ static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_te
"ModSecurity: Failed to initialise engine.");
return HTTP_INTERNAL_SERVER_ERROR;
}
log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
if (log_pfn_register) {
log_pfn_register(mp, "M", modsec_var_log_handler, 0);
@ -575,7 +575,6 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t
ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
"%s configured.", MODSEC_MODULE_NAME_FULL);
/* show libraries informations */
version(mp);
/* If we've changed the server signature make note of the original. */
@ -622,13 +621,13 @@ static int hook_request_early(request_rec *r) {
/* NOTE This check is not currently needed, but it may be needed in the
* future when we add another early phase.
*/
/* Are we allowed to continue? */
if (msr->txcfg->is_enabled == MODSEC_DISABLED) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Processing disabled, skipping (hook request_early).");
}
return DECLINED;
}
#endif
@ -668,7 +667,7 @@ static int hook_request_late(request_rec *r) {
msr_log(msr, 1, "Internal Error: Attempted to process the request body more than once.");
return DECLINED;
}
msr->phase_request_body_complete = 1;
msr->remote_user = r->user;
@ -693,15 +692,15 @@ static int hook_request_late(request_rec *r) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Processing disabled, skipping (hook request_late).");
}
return DECLINED;
}
/* Phase 1 */
/* Phase 1 */
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "First phase starting (dcfg %pp).", msr->dcfg2);
}
/* Process phase REQUEST_HEADERS */
if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) {
/* There was a match; see if we need to intercept. */
@ -709,18 +708,18 @@ static int hook_request_late(request_rec *r) {
if (rc != DECLINED) {
/* Intercepted */
return rc;
}
}
}
/* The rule engine could have been disabled in phase 1. */
/* The rule engine could have been disabled in phase 1. */
if (msr->txcfg->is_enabled == MODSEC_DISABLED) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Skipping phase 2 as the rule engine was disabled by a rule in phase 1.");
}
return DECLINED;
}
/* Phase 2 */
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Second phase starting (dcfg %pp).", msr->dcfg2);
@ -802,9 +801,16 @@ static int hook_request_late(request_rec *r) {
rc = perform_interception(msr);
}
if(msr->txcfg->stream_inbody_inspection && msr->msc_reqbody_read) {
const char *clen = NULL;
clen = apr_psprintf(msr->mp,"%d",msr->stream_input_length);
if(clen)
apr_table_setn(r->headers_in, "Content-Length",clen);
}
/* Remove the compression ability indications the client set,
* but only if we need to disable backend compression.
*/
*/
if (msr->txcfg->disable_backend_compression) {
apr_table_unset(r->headers_in, "Accept-Encoding");
apr_table_unset(r->headers_in, "TE");
@ -1221,13 +1227,13 @@ static void register_hooks(apr_pool_t *mp) {
"mod_ssl.c",
NULL
};
static const char *postconfig_afterme_list[] = {
"mod_fcgid.c",
"mod_cgid.c",
NULL
};
static const char *postread_beforeme_list[] = {
"mod_rpaf.c",
"mod_rpaf-2.0.c",
@ -1239,18 +1245,16 @@ static void register_hooks(apr_pool_t *mp) {
"mod_unique_id.c",
NULL
};
static const char *postread_afterme_list[] = {
"mod_log_forensic.c",
NULL
};
static const char *transaction_afterme_list[] = {
"mod_log_config.c",
NULL
};
/* Add the MODSEC_2.x compatibility defines */
*(char **)apr_array_push(ap_server_config_defines) = apr_pstrdup(mp, "MODSEC_2.5");
@ -1311,8 +1315,10 @@ static void register_hooks(apr_pool_t *mp) {
* mod_deflate = -1
* mod_headers = 0
*/
ap_register_output_filter("MODSECURITY_OUT", output_filter,
NULL, AP_FTYPE_CONTENT_SET - 3);
}
/* Defined in apache2_config.c */

View File

@ -343,7 +343,7 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) {
msr_log(msr, 1, "Initialisation: Error occurred while parsing QUERY_STRING arguments.");
return -1;
}
if (invalid_count) {
msr->urlencoded_error = 1;
}
@ -355,6 +355,8 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) {
if (msr->request_headers_to_sanitize == NULL) return -1;
msr->response_headers_to_sanitize = apr_table_make(msr->mp, 16);
if (msr->response_headers_to_sanitize == NULL) return -1;
msr->pattern_to_sanitize = apr_table_make(msr->mp, 32);
if (msr->pattern_to_sanitize == NULL) return -1;
/* Initialise cookies */
msr->request_cookies = apr_table_make(msr->mp, 16);
@ -543,6 +545,7 @@ static apr_status_t modsecurity_process_phase_response_body(modsec_rec *msr) {
msr->time_phase4 = apr_time_now() - time_before;
return rc;
}

View File

@ -32,6 +32,7 @@ typedef struct msc_engine msc_engine;
typedef struct msc_data_chunk msc_data_chunk;
typedef struct msc_arg msc_arg;
typedef struct msc_string msc_string;
typedef struct msc_parm msc_parm;
#include "msc_release.h"
#include "msc_logging.h"
@ -229,6 +230,11 @@ struct modsec_rec {
char *resbody_data;
unsigned int resbody_contains_html;
apr_size_t stream_input_length;
char *stream_input_data;
apr_size_t stream_output_length;
char *stream_output_data;
apr_array_header_t *error_messages;
apr_array_header_t *alerts;
@ -267,6 +273,7 @@ struct modsec_rec {
apr_table_t *request_headers_to_sanitize;
apr_table_t *response_headers_to_sanitize;
apr_table_t *request_cookies;
apr_table_t *pattern_to_sanitize;
unsigned int urlencoded_error;
unsigned int inbound_error;
@ -473,6 +480,10 @@ struct directory_config {
/* Content injection. */
int content_injection_enabled;
/* Stream Inspection */
int stream_inbody_inspection;
int stream_outbody_inspection;
/* Geo Lookup */
geo_db *geo;
@ -535,6 +546,11 @@ struct msc_string {
unsigned int value_len;
};
struct msc_parm {
char *value;
unsigned int pad_1;
unsigned int pad_2;
};
/* Engine functions */

View File

@ -343,15 +343,41 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro
/* NOTE: This is hard-coded for size 3 records */
/* Left */
if ((ipnum & (1 << level)) == 0) {
//rec_val = buf[0] +
// (buf[1] << 8) +
// (buf[2] << 16);
rec_val = (buf[3*0 + 0] << (0*8)) +
(buf[3*0 + 1] << (1*8)) +
(buf[3*0 + 2] << (2*8));
/*j = 3;
p = &buf[2*j];
x = 0;
do {
x <<= 8;
x += *(--p);
} while ( --j );
rec_val = x;
*/
}
/* Right */
else {
//rec_val = buf[3] +
// (buf[4] << 8) +
// (buf[5] << 16);
rec_val = (buf[3*1 + 0] << (0*8)) +
(buf[3*1 + 1] << (1*8)) +
(buf[3*1 + 2] << (2*8));
/*j = 3;
p = &buf[1*j];
x = 0;
do {
x <<= 8;
x += *(--p);
} while ( --j );
rec_val = x;
*/
}
/* If we are past the country offset, then we are done */

View File

@ -326,10 +326,16 @@ static char *create_auditlog_boundary(request_rec *r) {
* that have been marked as sensitive.
*/
static void sanitize_request_line(modsec_rec *msr) {
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
int i;
const apr_array_header_t *tarr = NULL;
const apr_table_entry_t *telts = NULL;
const apr_array_header_t *tarr_pattern = NULL;
const apr_table_entry_t *telts_pattern = NULL;
msc_parm *mparm = NULL;
int i, k;
char *qspos;
char *buf = NULL;
int sanitized_partial = 0;
int sanitize_matched = 0;
/* Locate the query string. */
qspos = strstr(msr->request_line, "?");
@ -343,8 +349,9 @@ static void sanitize_request_line(modsec_rec *msr) {
msc_arg *arg = (msc_arg *)telts[i].val;
/* Only look at the parameters that appeared in the query string. */
if (strcmp(arg->origin, "QUERY_STRING") == 0) {
char *pat = NULL;
char *p;
int j;
int j, arg_min, arg_max;
/* Go to the beginning of the parameter. */
p = qspos;
@ -352,23 +359,65 @@ static void sanitize_request_line(modsec_rec *msr) {
while((*p != '\0')&&(j--)) p++;
if (*p == '\0') {
msr_log(msr, 1, "Unable to sanitize variable \"%s\" at offset %u of QUERY_STRING"
"because the request line is too short.",
log_escape_ex(msr->mp, arg->name, arg->name_len),
arg->value_origin_offset);
"because the request line is too short.",
log_escape_ex(msr->mp, arg->name, arg->name_len),
arg->value_origin_offset);
continue;
}
/* Write over the value. */
j = arg->value_origin_len;
while((*p != '\0')&&(j--)) {
*p++ = '*';
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
sanitized_partial = 0;
sanitize_matched = 0;
buf = apr_psprintf(msr->mp, "%s",p);
for ( k = 0; k < tarr_pattern->nelts; k++) {
if(strncmp(telts_pattern[k].key,arg->name,strlen(arg->name)) ==0 ) {
mparm = (msc_parm *)telts_pattern[k].val;
pat = strstr(buf,mparm->value);
if(mparm->pad_1 == -1)
sanitize_matched = 1;
if (pat != NULL) {
j = strlen(mparm->value);
arg_min = j;
arg_max = 1;
while((*pat != '\0')&&(j--)) {
if(arg_max > mparm->pad_2) {
int off = (strlen(mparm->value) - arg_max);
int pos = (mparm->pad_1-1);
if(off > pos) {
*pat = '*';
}
}
arg_max++;
arg_min--;
*pat++;
}
}
sanitized_partial = 1;
}
}
if (*p == '\0') {
msr_log(msr, 1, "Unable to sanitize variable \"%s\" at offset %u (size %d) "
"of QUERY_STRING because the request line is too short.",
log_escape_ex(msr->mp, arg->name, arg->name_len),
arg->value_origin_offset, arg->value_origin_len);
if(sanitized_partial == 1 && sanitize_matched == 0) {
while(*buf != '\0') {
*p++ = *buf++;
}
continue;
} else {
/* Write over the value. */
j = arg->value_origin_len;
while((*p != '\0')&&(j--)) {
*p++ = '*';
}
if (*p == '\0') {
msr_log(msr, 1, "Unable to sanitize variable \"%s\" at offset %u (size %d) "
"of QUERY_STRING because the request line is too short.",
log_escape_ex(msr->mp, arg->name, arg->name_len),
arg->value_origin_offset, arg->value_origin_len);
continue;
}
}
}
}
@ -451,7 +500,7 @@ msre_rule *return_chained_rule(const msre_rule *current, modsec_rec *msr) {
for (i = 0; i < arr->nelts; i++) {
rule = rules[i];
if (rule != NULL) {
if (strcmp(current->unparsed,rule->unparsed) == 0) {
if (strncmp(current->unparsed,rule->unparsed,strlen(rule->unparsed)) == 0) {
if (i < arr->nelts -1) {
next_rule = rules[i+1];
} else {
@ -482,7 +531,7 @@ int chained_is_matched(modsec_rec *msr, const msre_rule *next_rule) {
for (i = 0; i < msr->matched_rules->nelts; i++) {
rule = ((msre_rule **)msr->matched_rules->elts)[i];
if (rule != NULL && (strcmp(rule->unparsed,next_rule->unparsed) == 0))
if (rule != NULL && (strncmp(rule->unparsed,next_rule->unparsed,strlen(next_rule->unparsed)) == 0))
return 1;
}
@ -495,6 +544,8 @@ int chained_is_matched(modsec_rec *msr, const msre_rule *next_rule) {
void sec_audit_logger(modsec_rec *msr) {
const apr_array_header_t *arr = NULL;
apr_table_entry_t *te = NULL;
const apr_array_header_t *tarr_pattern = NULL;
const apr_table_entry_t *telts_pattern = NULL;
char *str1 = NULL, *str2 = NULL, *text = NULL;
const msre_rule *rule = NULL, *next_rule = NULL;
apr_size_t nbytes, nbytes_written;
@ -504,7 +555,10 @@ void sec_audit_logger(modsec_rec *msr) {
int wrote_response_body = 0;
char *entry_filename, *entry_basename;
apr_status_t rc;
int i, limit;
int i, limit, k, sanitized_partial, j;
char *buf = NULL, *pat = NULL;
msc_parm *mparm = NULL;
int arg_min, arg_max, sanitize_matched;
/* the boundary is used by both audit log types */
msr->new_auditlog_boundary = create_auditlog_boundary(msr->r);
@ -621,12 +675,49 @@ void sec_audit_logger(modsec_rec *msr) {
arr = apr_table_elts(msr->request_headers);
te = (apr_table_entry_t *)arr->elts;
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
for (i = 0; i < arr->nelts; i++) {
sanitized_partial = 0;
sanitize_matched = 0;
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
/* Do we need to sanitize this request header? */
if (apr_table_get(msr->request_headers_to_sanitize, te[i].key) != NULL) {
/* Yes, sanitize it. */
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2);
for ( k = 0; k < tarr_pattern->nelts; k++) {
if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) {
mparm = (msc_parm *)telts_pattern[k].val;
if(mparm->pad_1 == -1)
sanitize_matched = 1;
pat = strstr(buf,mparm->value);
if (pat != NULL) {
j = strlen(mparm->value);
arg_min = j;
arg_max = 1;
while((*pat != '\0')&&(j--)) {
if(arg_max > mparm->pad_2) {
int off = strlen(mparm->value) - arg_max;
int pos = mparm->pad_1-1;
if(off > pos) {
*pat = '*';
}
}
arg_max++;
arg_min--;
*pat++;
}
sanitized_partial = 1;
}
}
}
if(sanitized_partial == 1 && sanitize_matched == 0) {
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, buf);
} else {
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
}
}
sec_auditlog_write(msr, text, strlen(text));
}
@ -820,10 +911,10 @@ void sec_audit_logger(modsec_rec *msr) {
if (msr->response_headers_sent) {
if (msr->status_line != NULL) {
text = apr_psprintf(msr->mp, "%s %s\n", msr->response_protocol,
msr->status_line);
msr->status_line);
} else {
text = apr_psprintf(msr->mp, "%s %u\n", msr->response_protocol,
msr->response_status);
msr->response_status);
}
sec_auditlog_write(msr, text, strlen(text));
@ -831,18 +922,57 @@ void sec_audit_logger(modsec_rec *msr) {
arr = apr_table_elts(msr->response_headers);
te = (apr_table_entry_t *)arr->elts;
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
for (i = 0; i < arr->nelts; i++) {
sanitized_partial = 0;
sanitize_matched = 0;
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
/* Do we need to sanitize this response header? */
if (apr_table_get(msr->response_headers_to_sanitize, te[i].key) != NULL) {
/* Yes, sanitize it. */
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2);
for ( k = 0; k < tarr_pattern->nelts; k++) {
if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) {
mparm = (msc_parm *)telts_pattern[k].val;
if(mparm->pad_1 == -1)
sanitize_matched = 1;
pat = strstr(buf,mparm->value);
if (pat != NULL) {
j = strlen(mparm->value);
arg_min = j;
arg_max = 1;
while((*pat != '\0')&&(j--)) {
if(arg_max > mparm->pad_2) {
int off = strlen(mparm->value) - arg_max;
int pos = mparm->pad_1-1;
if(off > pos) {
*pat = '*';
}
}
arg_max++;
arg_min--;
*pat++;
}
sanitized_partial = 1;
}
}
}
if(sanitized_partial == 1 && sanitize_matched == 0) {
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, buf);
} else {
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
}
}
sec_auditlog_write(msr, text, strlen(text));
}
}
}
apr_table_clear(msr->pattern_to_sanitize);
/* AUDITLOG_PART_RESPONSE_BODY */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_RESPONSE_BODY) != NULL) {
@ -887,19 +1017,19 @@ void sec_audit_logger(modsec_rec *msr) {
text = apr_psprintf(msr->mp, "Apache-Handler: %s\n", msr->r->handler);
sec_auditlog_write(msr, text, strlen(text));
}
/* Stopwatch; left in for compatibility reasons */
text = apr_psprintf(msr->mp, "Stopwatch: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT " (- - -)\n",
msr->request_time, (now - msr->request_time));
sec_auditlog_write(msr, text, strlen(text));
sec_auditlog_write(msr, text, strlen(text));
/* Stopwatch2 */
{
char *perf_all = format_all_performance_variables(msr, msr->mp);
text = apr_psprintf(msr->mp, "Stopwatch2: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT
"; %s\n", msr->request_time, (now - msr->request_time), perf_all);
sec_auditlog_write(msr, text, strlen(text));
}

View File

@ -307,7 +307,6 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr,
char *my_error_msg = NULL;
msre_reqbody_processor_metadata *metadata =
(msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor);
if (metadata != NULL) {
if ( (metadata->process != NULL)
@ -384,6 +383,51 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr,
return -1;
}
static apr_status_t modsecurity_request_body_to_stream(modsec_rec *msr, char **error_msg) {
msc_data_chunk **chunks;
char *d;
int i, sofar;
*error_msg = NULL;
/* Allocate a buffer large enough to hold the request body. */
if (msr->msc_reqbody_length + 1 == 0) {
*error_msg = apr_psprintf(msr->mp, "Internal error, request body length will overflow the stream buffer: %u",
msr->msc_reqbody_length);
return -1;
}
msr->stream_input_length = msr->msc_reqbody_length;
msr->stream_input_data = malloc(msr->stream_input_length + 1);
if (msr->stream_input_data== NULL) {
*error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body on stream. Asked for %u bytes.",
msr->stream_input_length + 1);
return -1;
}
msr->stream_input_data[msr->stream_input_length] = '\0';
/* Copy the data we keep in chunks into the new buffer. */
sofar = 0;
d = msr->stream_input_data;
chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
if (sofar + chunks[i]->length <= msr->stream_input_length) {
memcpy(d, chunks[i]->data, chunks[i]->length);
d += chunks[i]->length;
sofar += chunks[i]->length;
} else {
*error_msg = apr_psprintf(msr->mp, "Internal error, request body buffer overflow.");
return -1;
}
}
return 1;
}
/**
* Replace a bunch of chunks holding a request body with a single large chunk.
*/
@ -473,7 +517,7 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, cha
*error_msg = apr_pstrdup(msr->mp, "Initialisation: Error occurred while parsing BODY arguments.");
return -1;
}
if (invalid_count) {
msr->urlencoded_error = 1;
}
@ -498,12 +542,14 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) {
/* Note that we've read the body. */
msr->msc_reqbody_read = 1;
if (msr->txcfg->stream_inbody_inspection)
modsecurity_request_body_to_stream(msr, error_msg);
/* Finalise body processing. */
if ((msr->msc_reqbody_processor != NULL) && (msr->msc_reqbody_error == 0)) {
char *my_error_msg = NULL;
msre_reqbody_processor_metadata *metadata =
(msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor);
if (metadata != NULL) {
if ( (metadata->complete != NULL)

View File

@ -580,6 +580,8 @@ msre_actionset *msre_actionset_create(msre_engine *engine, const char *text,
actionset->phase = NOT_SET;
actionset->severity = -1;
actionset->rule = NOT_SET_P;
actionset->arg_max = -1;
actionset->arg_min = -1;
/* Flow */
actionset->is_chained = NOT_SET;
@ -660,6 +662,8 @@ msre_actionset *msre_actionset_merge(msre_engine *engine, msre_actionset *parent
if (child->severity != NOT_SET) merged->severity = child->severity;
if (child->phase != NOT_SET) merged->phase = child->phase;
if (child->rule != NOT_SET_P) merged->rule = child->rule;
if (child->arg_min != NOT_SET) merged->arg_min = child->arg_min;
if (child->arg_max != NOT_SET) merged->arg_max = child->arg_max;
/* Flow */
merged->is_chained = child->is_chained;
@ -714,6 +718,8 @@ void msre_actionset_set_defaults(msre_actionset *actionset) {
if (actionset->phase == NOT_SET) actionset->phase = 2;
if (actionset->severity == -1) {} /* leave at -1 */
if (actionset->rule == NOT_SET_P) actionset->rule = NULL;
if (actionset->arg_max == NOT_SET) actionset->arg_max = -1;
if (actionset->arg_min == NOT_SET) actionset->arg_min = -1;
/* Flow */
if (actionset->is_chained == NOT_SET) actionset->is_chained = 0;
@ -1839,7 +1845,7 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr,
time_before_op = apr_time_now();
}
#endif
rc = rule->op_metadata->execute(msr, rule, var, &my_error_msg);
#if defined(PERFORMANCE_MEASUREMENT)

View File

@ -16,6 +16,7 @@
* directly using the email address support@trustwave.com.
*
*/
#ifndef _MSC_RE_H_
#define _MSC_RE_H_
@ -174,6 +175,9 @@ struct msre_rule {
/* Compiled Lua script. */
msc_script *script;
#endif
ap_regex_t *sub_regex;
char *sub_str;
};
char DSOLOCAL *msre_rule_generate_unparsed(apr_pool_t *pool, const msre_rule *rule, const char *targets, const char *args, const char *actions);
@ -284,6 +288,8 @@ struct msre_actionset {
int severity;
int phase;
msre_rule *rule;
int arg_min;
int arg_max;
/* Flow */
int is_chained;

View File

@ -18,6 +18,9 @@
*/
#include "re.h"
#include <ctype.h>
#include "apr_lib.h"
#include "apr_strmatch.h"
/**
* Register action with the engine.
@ -344,7 +347,6 @@ apr_status_t collection_original_setvar(modsec_rec *msr, const char *col_name, c
msr_log(msr, 9, "Original collection variable: %s.%s = \"%s\"", col_name, var_name,
log_escape_ex(msr->mp, orig_var->value, orig_var->value_len));
}
return 1;
}
}
@ -406,6 +408,34 @@ static apr_status_t msre_action_logdata_init(msre_engine *engine, msre_actionset
return 1;
}
/* SanitizeMatchedBytes init */
static apr_status_t msre_action_sanitizeMatchedBytes_init(msre_engine *engine,
msre_actionset *actionset, msre_action *action)
{
char *parse_parm = NULL;
char *ac_param = NULL;
char *savedptr = NULL;
int arg_min = 0;
int arg_max = 0;
if (action->param != NULL && strlen(action->param) == 3) {
ac_param = apr_pstrdup(engine->mp, action->param);
parse_parm = apr_strtok(ac_param,"/",&savedptr);
if(apr_isdigit(*parse_parm) && apr_isdigit(*savedptr)) {
arg_max = atoi(parse_parm);
arg_min = atoi(savedptr);
}
}
actionset->arg_min = arg_min;
actionset->arg_max = arg_max;
return 1;
}
/* severity */
static apr_status_t msre_action_severity_init(msre_engine *engine,
@ -1053,7 +1083,9 @@ static apr_status_t msre_action_sanitizeMatched_execute(modsec_rec *msr, apr_poo
const char *sargname = NULL;
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
int i, type = 0;
const apr_array_header_t *tarr_pattern;
const apr_table_entry_t *telts_pattern;
int i, type = 0, k;
msc_string *mvar = msr->matched_var;
if (mvar->name_len == 0) return 0;
@ -1090,7 +1122,6 @@ static apr_status_t msre_action_sanitizeMatched_execute(modsec_rec *msr, apr_poo
msr_log(msr, 3, "sanitizeMatched: Don't know how to handle variable: %s",
mvar->name);
}
return 0;
}
@ -2293,7 +2324,20 @@ void msre_engine_register_default_actions(msre_engine *engine) {
NULL,
msre_action_sanitizeArg_execute
);
/* sanitiseMatchedBytes */
msre_engine_action_register(engine,
"sanitizeMatchedBytes",
ACTION_NON_DISRUPTIVE,
0, 1,
NO_PLUS_MINUS,
ACTION_CARDINALITY_MANY,
ACTION_CGROUP_NONE,
NULL,
msre_action_sanitizeMatchedBytes_init,
msre_action_sanitizeMatched_execute
);
/* sanitizeArg */
msre_engine_action_register(engine,
"sanitizeArg",
@ -2319,7 +2363,7 @@ void msre_engine_register_default_actions(msre_engine *engine) {
NULL,
msre_action_sanitizeMatched_execute
);
/* sanitizeMatched */
msre_engine_action_register(engine,
"sanitizeMatched",

View File

@ -22,6 +22,7 @@
#include "apr_lib.h"
#include "apr_strmatch.h"
#include "acmp.h"
#include <regex.h>
/**
*
@ -72,6 +73,162 @@ static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule,
return 0;
}
/* rsub */
static int msre_op_rsub_param_init(msre_rule *rule, char **error_msg) {
const char *errptr = NULL;
int erroffset;
ap_regex_t *regex;
const char *pattern = NULL;
const char *line = NULL;
char *reg_pattern = NULL;
char *replace = NULL;
char *flags = NULL;
char *data;
char delim;
int ignore_case = 0;
line = rule->op_param;
if (apr_tolower(*line) != 's') {
*error_msg = apr_psprintf(rule->ruleset->mp, "Error rsub operator format, must be s/// pattern",
erroffset, errptr);
return 0;
}
data = apr_pstrdup(rule->ruleset->mp, line);
delim = *++data;
if (delim)
reg_pattern = ++data;
if (reg_pattern) {
if (*data != delim) {
while (*++data && *data != delim);
}
if (*data) {
*data = '\0';
replace = ++data;
}
}
if (replace) {
if (*data != delim) {
while (*++data && *data != delim);
}
if (*data) {
*data = '\0';
flags = ++data;
}
}
if (!delim || !reg_pattern || !*reg_pattern || !replace) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Error rsub operator format - must be s/regex/str/[flags]",
erroffset, errptr);
return 0;
}
if (flags) {
while (*flags) {
delim = apr_tolower(*flags);
if (delim == 'i')
ignore_case = 1;
else
*error_msg = apr_psprintf(rule->ruleset->mp, "Regex flag not supported",
erroffset, errptr);
flags++;
}
}
if (error_msg == NULL) return -1;
*error_msg = NULL;
pattern = apr_pstrdup(rule->ruleset->mp, reg_pattern);
rule->sub_str = apr_pstrdup(rule->ruleset->mp, replace);
regex = ap_pregcomp(rule->ruleset->mp, pattern, AP_REG_EXTENDED |
(ignore_case ? AP_REG_ICASE : 0));
rule->sub_regex = regex;
return 1; /* OK */
}
static int msre_op_rsub_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
ap_regex_t *regex = rule->sub_regex;
char *offset = NULL;
int sub = 0, so = 0, p_len = 0;
char *replace = NULL;
char *data = NULL;
int size = var->value_len;
int output_body = 0, input_body = 0, count = 0;
ap_regmatch_t pmatch[AP_MAX_REG_MATCH];
if(strcmp(var->name,"STREAM_OUTPUT_BODY") == 0 ) {
output_body = 1;
} else if(strcmp(var->name,"STREAM_INPUT_BODY") == 0 ) {
input_body = 1;
} else {
msr_log(msr,9,"Operator rsub only works with STREAM_* variables");
return -1;
}
replace = apr_pstrdup(rule->ruleset->mp, rule->sub_str);;
data = apr_pcalloc(msr->mp, var->value_len+(AP_MAX_REG_MATCH*strlen(replace))+1);
if(replace == NULL || data == NULL)
return 0;
memcpy(data,var->value,var->value_len);
size += (AP_MAX_REG_MATCH*strlen(replace)+2);
if (ap_regexec(rule->sub_regex, data ,AP_MAX_REG_MATCH, pmatch, 0)) return 0;
for (offset = replace; *offset; offset++)
if (*offset == '\\' && *(offset + 1) > '0' && *(offset + 1) <= '9') {
so = pmatch [*(offset + 1) - 48].rm_so;
p_len = pmatch [*(offset + 1) - 48].rm_eo - so;
if (so < 0 || strlen (replace) + p_len - 1 > size) return 0;
memmove (offset + p_len, offset + 2, strlen (offset) - 1);
memmove (offset, data + so, p_len);
offset = offset + p_len - 2;
}
sub = pmatch [1].rm_so;
for (offset = data; !ap_regexec(rule->sub_regex, offset, 1, pmatch, 0); ) {
p_len = pmatch [0].rm_eo - pmatch [0].rm_so;
count++;
offset += pmatch [0].rm_so;
if (var->value_len - p_len + strlen(replace) + 1 > size) return 0;
memmove (offset + strlen (replace), offset + p_len, strlen (offset) - p_len + 1);
memmove (offset, replace, strlen (replace));
offset += strlen (replace);
if (sub >= 0) break;
}
size -= ((AP_MAX_REG_MATCH - count)*(strlen(replace)) + ((strlen(replace) - p_len)*(count+AP_MAX_REG_MATCH) - (AP_MAX_REG_MATCH+4)));
if(msr->stream_output_data != NULL && output_body == 1) {
msr->stream_output_data = (char *)realloc(msr->stream_output_data,size);
msr->stream_output_length = size;
if (msr->stream_output_data != NULL) {
memset(msr->stream_output_data,0,size);
memcpy(msr->stream_output_data,data,size);
msr->stream_output_data[msr->stream_output_length] = '\0';
}
}
if(msr->stream_input_data != NULL && input_body == 1) {
msr->stream_input_data = (char *)realloc(msr->stream_input_data,size);
msr->stream_input_length = size;
if (msr->stream_input_data != NULL) {
memset(msr->stream_input_data,0,size);
memcpy(msr->stream_input_data,data,size);
msr->stream_input_data[msr->stream_input_length] = '\0';
}
}
return 1;
}
/* rx */
static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) {
@ -103,7 +260,12 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c
char *my_error_msg = NULL;
int ovector[33];
int capture = 0;
int matched_bytes = 0;
int matched = 0;
int rc;
char *qspos = NULL;
const char *parm = NULL;
msc_parm *mparm = NULL;
if (error_msg == NULL) return -1;
*error_msg = NULL;
@ -127,12 +289,14 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c
/* Are we supposed to capture subexpressions? */
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0;
matched = apr_table_get(rule->actionset->actions, "sanitizeMatched") ? 1 : 0;
/* Show when the regex captures but "capture" is not set */
if (msr->txcfg->debuglog_level >= 6) {
int capcount = 0;
rc = msc_fullinfo(regex, PCRE_INFO_CAPTURECOUNT, &capcount);
if (msr->txcfg->debuglog_level >= 6) {
if (msr->txcfg->debuglog_level >= 6) {
if ((capture == 0) && (capcount > 0)) {
msr_log(msr, 6, "Ignoring regex captures since \"capture\" action is not enabled.");
}
@ -192,16 +356,39 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c
for(i = 0; i < rc; i++) {
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
if (s == NULL) return -1;
s->name = apr_psprintf(msr->mp, "%d", i);
s->name_len = strlen(s->name);
s->value = apr_pstrmemdup(msr->mp,
target + ovector[2 * i], ovector[2 * i + 1] - ovector[2 * i]);
s->value_len = (ovector[2 * i + 1] - ovector[2 * i]);
if ((s->name == NULL)||(s->value == NULL)) return -1;
apr_table_addn(msr->tx_vars, s->name, (void *)s);
if(((matched == 1) || (matched_bytes == 1)) && (var != NULL) && (var->name != NULL)) {
qspos = apr_psprintf(msr->mp, "%s", var->name);
parm = strstr(qspos, ":");
if (parm != NULL) {
parm++;
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
if (mparm == NULL)
continue;
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
mparm->pad_1 = rule->actionset->arg_min;
mparm->pad_2 = rule->actionset->arg_max;
apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm);
} else {
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
if (mparm == NULL)
continue;
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm);
}
}
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
log_escape_nq_ex(msr->mp, s->value, s->value_len));
@ -2290,6 +2477,13 @@ void msre_engine_register_default_operators(msre_engine *engine) {
msre_op_nomatch_execute
);
/* rsub */
msre_engine_op_register(engine,
"rsub",
msre_op_rsub_param_init,
msre_op_rsub_execute
);
/* rx */
msre_engine_op_register(engine,
"rx",

View File

@ -2149,6 +2149,32 @@ static int var_path_info_generate(modsec_rec *msr, msre_var *var, msre_rule *rul
return var_simple_generate(var, vartab, mptmp, value);
}
/* STREAM_OUTPUT_BODY */
static int var_stream_output_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
apr_table_t *vartab, apr_pool_t *mptmp)
{
if (msr->stream_output_data != NULL) {
return var_simple_generate_ex(var, vartab, mptmp,
msr->stream_output_data, msr->stream_output_length);
}
return 0;
}
/* STREAM_INPUT_BODY */
static int var_stream_input_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
apr_table_t *vartab, apr_pool_t *mptmp)
{
if (msr->stream_input_data != NULL) {
return var_simple_generate_ex(var, vartab, mptmp,
msr->stream_input_data, msr->stream_input_length);
}
return 0;
}
/* RESPONSE_BODY */
static int var_response_body_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
@ -2890,7 +2916,7 @@ void msre_engine_register_default_variables(msre_engine *engine) {
VAR_CACHE,
PHASE_REQUEST_BODY
);
/* REQUEST_BODY_LENGTH */
msre_engine_variable_register(engine,
"REQUEST_BODY_LENGTH",
@ -3012,6 +3038,29 @@ void msre_engine_register_default_variables(msre_engine *engine) {
PHASE_REQUEST_HEADERS
);
/* STREAM_OUTPUT_BODY */
msre_engine_variable_register(engine,
"STREAM_OUTPUT_BODY",
VAR_SIMPLE,
0, 0,
NULL,
var_stream_output_generate,
VAR_CACHE,
PHASE_RESPONSE_BODY
);
/* STREAM_INPUT_BODY */
msre_engine_variable_register(engine,
"STREAM_INPUT_BODY",
VAR_SIMPLE,
0, 0,
NULL,
var_stream_input_generate,
VAR_CACHE,
PHASE_FIRST
);
/* RESPONSE_BODY */
msre_engine_variable_register(engine,
"RESPONSE_BODY",