/** * Handle request_header events for remote IP extraction. * * Extract the "request_headers" field (a list) from the transactions's * data provider instance, then loop through the list, looking for the * "X-Forwarded-For" field. If found, the first value in the (comma * separated) list replaces the local ip address string in the connection * object. * * @param[in] ib IronBee object * @param[in,out] tx Transaction object * @param[in] event Event type * @param[in] cbdata Callback data (not used) * * @returns Status code */ static ib_status_t modua_remoteip(ib_engine_t *ib, ib_tx_t *tx, ib_state_event_type_t event, void *cbdata) { IB_FTRACE_INIT(); assert(ib != NULL); assert(tx != NULL); assert(tx->dpi != NULL); assert(event == request_header_finished_event); ib_field_t *field = NULL; ib_status_t rc = IB_OK; const ib_bytestr_t *bs; const uint8_t *data; size_t len; char *buf; uint8_t *comma; const ib_list_t *list; const ib_list_node_t *node; const ib_field_t *forwarded; uint8_t *stripped; size_t num; ib_flags_t flags; ib_log_debug3_tx(tx, "Checking for alternate remote address"); /* Extract the X-Forwarded-For from the provider instance */ rc = ib_data_get(tx->dpi, "request_headers:X-Forwarded-For", &field); if ( (field == NULL) || (rc != IB_OK) ) { ib_log_debug_tx(tx, "No X-Forwarded-For field"); IB_FTRACE_RET_STATUS(IB_OK); } /* Because we asked for a filtered item, what we get back is a list */ rc = ib_field_value(field, ib_ftype_list_out(&list)); if (rc != IB_OK) { ib_log_debug_tx(tx, "No request header collection"); IB_FTRACE_RET_STATUS(rc); } num = ib_list_elements(list); if (num == 0) { ib_log_debug_tx(tx, "No X-Forwarded-For header found"); IB_FTRACE_RET_STATUS(rc); } else if (num != 1) { ib_log_debug_tx(tx, "%zd X-Forwarded-For headers found: ignoring", num); IB_FTRACE_RET_STATUS(rc); } node = ib_list_last_const(list); if ( (node == NULL) || (node->data == NULL) ) { ib_log_notice_tx(tx, "Invalid X-Forwarded-For header found"); IB_FTRACE_RET_STATUS(rc); } forwarded = (const ib_field_t *)node->data; /* Found it: copy the data into a newly allocated string buffer */ rc = ib_field_value_type(forwarded, ib_ftype_bytestr_out(&bs), IB_FTYPE_BYTESTR); if (rc != IB_OK) { ib_log_notice_tx(tx, "Invalid X-Forwarded-For header value"); IB_FTRACE_RET_STATUS(rc); } if (bs == NULL) { ib_log_notice_tx(tx, "X-Forwarded-For header not a bytestr"); IB_FTRACE_RET_STATUS(IB_EINVAL); } len = ib_bytestr_length(bs); data = ib_bytestr_const_ptr(bs); /* Search for a comma in the buffer */ comma = memchr(data, ',', len); if (comma != NULL) { len = comma - data; } /* Trim whitespace */ stripped = (uint8_t *)data; rc = ib_strtrim_lr_ex(IB_STROP_INPLACE, tx->mp, stripped, len, &stripped, &len, &flags); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } /* Verify that it looks like a valid IP v4/6 address */ rc = ib_ip_validate_ex((const char *)stripped, len); if (rc != IB_OK) { ib_log_error_tx(tx, "X-Forwarded-For \"%.*s\" is not a valid IP address", (int)len, stripped ); IB_FTRACE_RET_STATUS(IB_OK); } /* Allocate memory for copy of stripped string */ buf = (char *)ib_mpool_alloc(tx->mp, len+1); if (buf == NULL) { ib_log_error_tx(tx, "Failed to allocate %zd bytes for remote address", len+1); IB_FTRACE_RET_STATUS(IB_EALLOC); } /* Copy the string out */ memcpy(buf, stripped, len); buf[len] = '\0'; ib_log_info_tx(tx, "Remote address changed to \"%s\"", buf); /* This will lose the pointer to the original address * buffer, but it should be cleaned up with the rest * of the memory pool. */ tx->er_ipstr = buf; /* Update the remote address field in the tx collection */ rc = ib_data_add_bytestr(tx->dpi, "remote_addr", (uint8_t*)buf, len, NULL); if (rc != IB_OK) { ib_log_error_tx(tx, "Failed to create remote address TX field: %s", ib_status_to_string(rc)); IB_FTRACE_RET_STATUS(rc); } IB_FTRACE_RET_STATUS(IB_OK); }
/** * @brief Execute the PCRE operator * * @param[in] tx Current transaction. * @param[in] instance_data Instance data needed for execution. * @param[in] field The field to operate on. * @param[in] capture If non-NULL, the collection to capture to. * @param[out] result The result of the operator 1=true 0=false. * @param[in] cbdata Callback data. * * @returns IB_OK most times. IB_EALLOC when a memory allocation error handles. */ static ib_status_t pcre_operator_execute( ib_tx_t *tx, void *instance_data, const ib_field_t *field, ib_field_t *capture, ib_num_t *result, void *cbdata ) { assert(instance_data != NULL); assert(tx != NULL); int matches; ib_status_t ib_rc; const int ovecsize = 3 * MATCH_MAX; int *ovector = (int *)malloc(ovecsize*sizeof(*ovector)); const char *subject = NULL; size_t subject_len = 0; const ib_bytestr_t *bytestr; modpcre_operator_data_t *operator_data = (modpcre_operator_data_t *)instance_data; pcre_extra *edata = NULL; #ifdef PCRE_JIT_STACK pcre_jit_stack *jit_stack = NULL; #endif assert(operator_data->cpdata->is_dfa == false); if (ovector==NULL) { return IB_EALLOC; } if (field->type == IB_FTYPE_NULSTR) { ib_rc = ib_field_value(field, ib_ftype_nulstr_out(&subject)); if (ib_rc != IB_OK) { free(ovector); return ib_rc; } if (subject != NULL) { subject_len = strlen(subject); } } else if (field->type == IB_FTYPE_BYTESTR) { ib_rc = ib_field_value(field, ib_ftype_bytestr_out(&bytestr)); if (ib_rc != IB_OK) { free(ovector); return ib_rc; } if (bytestr != NULL) { subject_len = ib_bytestr_length(bytestr); subject = (const char *) ib_bytestr_const_ptr(bytestr); } } else { free(ovector); return IB_EINVAL; } if (subject == NULL) { subject = ""; } if (operator_data->cpdata->is_jit) { #ifdef PCRE_JIT_STACK jit_stack = pcre_jit_stack_alloc(operator_data->cpdata->jit_stack_start, operator_data->cpdata->jit_stack_max); if (jit_stack == NULL) { ib_log_warn(ib, "Failed to allocate a jit stack for a jit-compiled rule. " "Not using jit for this call." ); } /* If the study data is NULL or size zero, don't use it. */ else if (operator_data->cpdata->study_data_sz > 0) { edata = operator_data->cpdata->edata; } if (edata != NULL) { pcre_assign_jit_stack(edata, NULL, jit_stack); } #else edata = NULL; #endif } else if (operator_data->cpdata->study_data_sz > 0) { edata = operator_data->cpdata->edata; } else { edata = NULL; } matches = pcre_exec(operator_data->cpdata->cpatt, edata, subject, subject_len, 0, /* Starting offset. */ 0, /* Options. */ ovector, ovecsize); #ifdef PCRE_JIT_STACK if (jit_stack != NULL) { pcre_jit_stack_free(jit_stack); } #endif if (matches > 0) { if (capture != NULL) { pcre_set_matches(tx, capture, ovector, matches, subject); } ib_rc = IB_OK; *result = 1; } else if (matches == PCRE_ERROR_NOMATCH) { ib_rc = IB_OK; *result = 0; } else { /* Some other error occurred. Set the status to false return the * error. */ ib_rc = IB_EUNKNOWN; *result = 0; } free(ovector); return ib_rc; }
/** * Handle request_header events for user agent extraction. * * Extract the "request_headers" field (a list) from the transactions's * data provider instance, then loop through the list, looking for the * "User-Agent" field. If found, the value is parsed and used to update the * connection object fields. * * @param[in] ib IronBee object * @param[in,out] tx Transaction. * @param[in] event Event type * @param[in] data Callback data (not used) * * @returns Status code */ static ib_status_t modua_user_agent(ib_engine_t *ib, ib_tx_t *tx, ib_state_event_type_t event, void *data) { IB_FTRACE_INIT(); assert(ib != NULL); assert(tx != NULL); assert(tx->dpi != NULL); assert(event == request_header_finished_event); ib_field_t *req_agent = NULL; ib_status_t rc = IB_OK; const ib_list_t *bs_list; const ib_bytestr_t *bs; /* Extract the User-Agent header field from the provider instance */ rc = ib_data_get(tx->dpi, "request_headers:User-Agent", &req_agent); if ( (req_agent == NULL) || (rc != IB_OK) ) { ib_log_debug_tx(tx, "request_header_finished_event: No user agent"); IB_FTRACE_RET_STATUS(IB_OK); } if (req_agent->type != IB_FTYPE_LIST) { ib_log_error_tx(tx, "Expected request_headers:User-Agent to " "return list of values."); IB_FTRACE_RET_STATUS(IB_EINVAL); } rc = ib_field_value_type(req_agent, ib_ftype_list_out(&bs_list), IB_FTYPE_LIST); if (rc != IB_OK) { ib_log_error_tx(tx, "Cannot retrieve request_headers:User-Agent: %d", rc); IB_FTRACE_RET_STATUS(rc); } if (IB_LIST_ELEMENTS(bs_list) == 0) { ib_log_debug_tx(tx, "request_header_finished_event: No user agent"); IB_FTRACE_RET_STATUS(IB_OK); } req_agent = (ib_field_t *)IB_LIST_NODE_DATA(IB_LIST_LAST(bs_list)); /* Found it: copy the data into a newly allocated string buffer */ rc = ib_field_value_type(req_agent, ib_ftype_bytestr_out(&bs), IB_FTYPE_BYTESTR); if (rc != IB_OK) { ib_log_error_tx(tx, "Request user agent is not a BYTESTR: %s", ib_status_to_string(rc)); IB_FTRACE_RET_STATUS(rc); } /* Finally, split it up & store the components */ rc = modua_agent_fields(ib, tx, bs); IB_FTRACE_RET_STATUS(rc); }
/** * Execute the @c ee_match_any operator. * * At first match the operator will stop searching and return true. * * The capture option is supported; the matched pattern will be placed in the * capture variable if a match occurs. * * @param[in] tx Current transaction. * @param[in] instance_data Instance data needed for execution. * @param[in] field The field to operate on. * @param[in] capture If non-NULL, the collection to capture to. * @param[out] result The result of the operator 1=true 0=false. * @param[in] cbdata Callback data. */ static ib_status_t ee_match_any_operator_execute( ib_tx_t *tx, void *instance_data, const ib_field_t *field, ib_field_t *capture, ib_num_t *result, void *cbdata ) { ib_status_t rc; ia_eudoxus_result_t ia_rc; ee_operator_data_t *operator_data = instance_data; ia_eudoxus_t* eudoxus = operator_data->eudoxus; ia_eudoxus_state_t* state = NULL; const char *input; size_t input_len; ee_callback_data_t *ee_cbdata; const ib_module_t *m = (const ib_module_t *)cbdata; assert(m != NULL); assert(tx != NULL); assert(instance_data != NULL); *result = 0; if (field->type == IB_FTYPE_NULSTR) { rc = ib_field_value(field, ib_ftype_nulstr_out(&input)); if (rc != IB_OK) { return rc; } input_len = strlen(input); } else if (field->type == IB_FTYPE_BYTESTR) { const ib_bytestr_t *bs; rc = ib_field_value(field, ib_ftype_bytestr_out(&bs)); if (rc != IB_OK) { return rc; } input = (const char *)ib_bytestr_const_ptr(bs); input_len = ib_bytestr_length(bs); } else if (field->type == IB_FTYPE_LIST) { return IB_ENOTIMPL; } else { return IB_EINVAL; } rc = get_ee_tx_data(m, tx, operator_data, &state); if (rc == IB_ENOENT) { /* State not found create it */ ee_cbdata = ib_mpool_alloc(tx->mp, sizeof(*ee_cbdata)); if (ee_cbdata == NULL) { return IB_EALLOC; } ee_cbdata->tx = tx; ee_cbdata->capture = capture; ia_rc = ia_eudoxus_create_state(&state, eudoxus, ee_first_match_callback, (void *)ee_cbdata); if (ia_rc != IA_EUDOXUS_OK) { if (state != NULL) { ia_eudoxus_destroy_state(state); state = NULL; } return IB_EINVAL; } set_ee_tx_data(m, tx, operator_data, state); } else if (rc != IB_OK) { /* Error getting the state -- abort */ return rc; } rc = IB_OK; ia_rc = ia_eudoxus_execute(state, (const uint8_t *)input, input_len); if (ia_rc == IA_EUDOXUS_STOP) { *result = 1; rc = IB_OK; } else if (ia_rc == IA_EUDOXUS_ERROR) { rc = IB_EUNKNOWN; } return rc; }
/** * @brief Execute the dfa operator * * @param[in] tx Current transaction. * @param[in] instance_data Instance data needed for execution. * @param[in] field The field to operate on. * @param[in] capture If non-NULL, the collection to capture to. * @param[out] result The result of the operator 1=true 0=false. * @param[in] cbdata Callback data. * * @returns IB_OK most times. IB_EALLOC when a memory allocation error handles. */ static ib_status_t dfa_operator_execute( ib_tx_t *tx, void *instance_data, const ib_field_t *field, ib_field_t *capture, ib_num_t *result, void *cbdata ) { assert(instance_data != NULL); assert(tx != NULL); int matches; ib_status_t ib_rc; const int ovecsize = 3 * MATCH_MAX; modpcre_operator_data_t *operator_data = (modpcre_operator_data_t *)instance_data; int *ovector; const char *subject; size_t subject_len; const ib_bytestr_t *bytestr; dfa_workspace_t *dfa_workspace; const char *id = operator_data->id; int options; /* dfa exec options. */ int start_offset; int match_count; const ib_module_t *m = (const ib_module_t *)cbdata; assert(m != NULL); assert(operator_data->cpdata->is_dfa == true); ovector = (int *)malloc(ovecsize*sizeof(*ovector)); if (ovector==NULL) { return IB_EALLOC; } if (field->type == IB_FTYPE_NULSTR) { ib_rc = ib_field_value(field, ib_ftype_nulstr_out(&subject)); if (ib_rc != IB_OK) { free(ovector); return ib_rc; } subject_len = strlen(subject); } else if (field->type == IB_FTYPE_BYTESTR) { ib_rc = ib_field_value(field, ib_ftype_bytestr_out(&bytestr)); if (ib_rc != IB_OK) { free(ovector); return ib_rc; } subject_len = ib_bytestr_length(bytestr); subject = (const char *) ib_bytestr_const_ptr(bytestr); } else { free(ovector); return IB_EINVAL; } /* Get the per-tx workspace data for this rule data id. */ ib_rc = get_dfa_tx_data(m, tx, id, &dfa_workspace); if (ib_rc == IB_ENOENT) { /* First time we are called, clear the captures. */ if (capture) { ib_rc = ib_capture_clear(capture); if (ib_rc != IB_OK) { ib_log_error_tx(tx, "Error clearing captures: %s", ib_status_to_string(ib_rc)); } } options = PCRE_PARTIAL_SOFT; ib_rc = alloc_dfa_tx_data(m, tx, operator_data->cpdata, id, &dfa_workspace); if (ib_rc != IB_OK) { free(ovector); return ib_rc; } } else if (ib_rc == IB_OK) { options = PCRE_PARTIAL_SOFT | PCRE_DFA_RESTART; } else { free(ovector); return ib_rc; } /* Perform the match. * If capturing is specified, then find all matches. */ start_offset = 0; match_count = 0; do { matches = pcre_dfa_exec(operator_data->cpdata->cpatt, operator_data->cpdata->edata, subject, subject_len, start_offset, /* Starting offset. */ options, ovector, ovecsize, dfa_workspace->workspace, dfa_workspace->wscount); if (matches > 0) { ++match_count; /* Use the longest match - the first in ovector - * to set the offset in the subject for the next * match. */ start_offset = ovector[1] + 1; if (capture) { pcre_dfa_set_match(tx, capture, ovector, 1, subject); } } } while (capture && (matches > 0)); if (match_count > 0) { ib_rc = IB_OK; *result = 1; } else if ((matches == 0) || (matches == PCRE_ERROR_NOMATCH)) { ib_rc = IB_OK; *result = 0; } else if (matches == PCRE_ERROR_PARTIAL) { ib_rc = IB_OK; *result = 0; } else { /* Some other error occurred. Set the status to false and * return the error. */ ib_rc = IB_EUNKNOWN; *result = 0; } free(ovector); return ib_rc; }
/** * @brief Execute the rule. * * @param[in] ib Ironbee engine * @param[in] tx The transaction. * @param[in,out] User data. A @c pcre_rule_data_t. * @param[in] flags Operator instance flags * @param[in] field The field content. * @param[in] result The result. * @returns IB_OK most times. IB_EALLOC when a memory allocation error handles. */ static ib_status_t pcre_operator_execute(ib_engine_t *ib, ib_tx_t *tx, void *data, ib_flags_t flags, ib_field_t *field, ib_num_t *result) { IB_FTRACE_INIT(); assert(ib!=NULL); assert(tx!=NULL); assert(tx->dpi!=NULL); assert(data!=NULL); int matches; ib_status_t ib_rc; const int ovecsize = 3 * MATCH_MAX; int *ovector = (int *)malloc(ovecsize*sizeof(*ovector)); const char* subject; size_t subject_len; const ib_bytestr_t* bytestr; pcre_rule_data_t *rule_data = (pcre_rule_data_t *)data; pcre *regex; pcre_extra *regex_extra = NULL; #ifdef PCRE_JIT_STACK pcre_jit_stack *jit_stack = pcre_jit_stack_alloc(PCRE_JIT_MIN_STACK_SZ, PCRE_JIT_MAX_STACK_SZ); #endif if (ovector==NULL) { IB_FTRACE_RET_STATUS(IB_EALLOC); } if (field->type == IB_FTYPE_NULSTR) { ib_rc = ib_field_value(field, ib_ftype_nulstr_out(&subject)); if (ib_rc != IB_OK) { IB_FTRACE_RET_STATUS(ib_rc); } subject_len = strlen(subject); } else if (field->type == IB_FTYPE_BYTESTR) { ib_rc = ib_field_value(field, ib_ftype_bytestr_out(&bytestr)); if (ib_rc != IB_OK) { IB_FTRACE_RET_STATUS(ib_rc); } subject_len = ib_bytestr_length(bytestr); subject = (const char *) ib_bytestr_const_ptr(bytestr); } else { free(ovector); IB_FTRACE_RET_STATUS(IB_EALLOC); } /* Debug block. Escapes a string and prints it to the log. * Memory is freed. */ if (ib_log_get_level(ib) >= 9) { /* Worst case, we can have a string that is 4x larger. * Consider if a string of 0xF7 is passed. That single character * will expand to a string of 4 printed characters +1 for the \0 * character. */ char *debug_str = ib_util_hex_escape(subject, subject_len); if ( debug_str != NULL ) { ib_log_debug3_tx(tx, "Matching against: %s", debug_str); free( debug_str ); } } /* Alloc space to copy regex. */ regex = (pcre *)malloc(rule_data->cpatt_sz); if (regex == NULL ) { free(ovector); IB_FTRACE_RET_STATUS(IB_EALLOC); } memcpy(regex, rule_data->cpatt, rule_data->cpatt_sz); if (rule_data->study_data_sz == 0 ) { regex_extra = NULL; } else { regex_extra = (pcre_extra *) malloc(sizeof(*regex_extra)); if (regex_extra == NULL ) { free(ovector); free(regex); IB_FTRACE_RET_STATUS(IB_EALLOC); } *regex_extra = *rule_data->edata; if ( rule_data->study_data_sz == 0 ) { regex_extra->study_data = NULL; } else { regex_extra->study_data = malloc(rule_data->study_data_sz); if (regex_extra->study_data == NULL ) { free(ovector); if (regex_extra != NULL) { free(regex_extra); } free(regex); IB_FTRACE_RET_STATUS(IB_EALLOC); } memcpy(regex_extra->study_data, rule_data->edata->study_data, rule_data->study_data_sz); } /* Put some modest limits on our regex. */ regex_extra->match_limit = 1000; regex_extra->match_limit_recursion = 1000; regex_extra->flags = regex_extra->flags | PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION; } #ifdef PCRE_JIT_STACK if (jit_stack == NULL) { if ( regex_extra != NULL ) { if ( regex_extra->study_data != NULL ) { free(regex_extra->study_data); } free(regex_extra); } free(ovector); free(regex); IB_FTRACE_RET_STATUS(IB_EALLOC); } pcre_assign_jit_stack(regex_extra, NULL, jit_stack); #endif matches = pcre_exec(regex, regex_extra, subject, subject_len, 0, /* Starting offset. */ 0, /* Options. */ ovector, ovecsize); #ifdef PCRE_JIT_STACK pcre_jit_stack_free(jit_stack); #endif if (matches > 0) { pcre_set_matches(ib, tx, "TX", ovector, matches, subject); ib_rc = IB_OK; *result = 1; } else if (matches == PCRE_ERROR_NOMATCH) { if (ib_log_get_level(ib) >= 7) { char* tmp_c = malloc(subject_len+1); memcpy(tmp_c, subject, subject_len); tmp_c[subject_len] = '\0'; /* No match. Return false to the caller (*result = 0). */ ib_log_debug2_tx(tx, "No match for [%s] using pattern [%s].", tmp_c, rule_data->patt); free(tmp_c); } ib_rc = IB_OK; *result = 0; } else { /* Some other error occurred. Set the status to false and report the error. */ ib_rc = IB_EUNKNOWN; *result = 0; } if ( regex_extra != NULL ) { if ( regex_extra->study_data != NULL ) { free(regex_extra->study_data); } free(regex_extra); } free(ovector); free(regex); IB_FTRACE_RET_STATUS(ib_rc); }
/** * Handle request_header events for remote IP extraction. * * Extract the "request_headers" field (a list) from the transactions's * data provider instance, then loop through the list, looking for the * "X-Forwarded-For" field. If found, the first value in the (comma * separated) list replaces the local ip address string in the connection * object. * * @param[in] ib IronBee object * @param[in,out] tx Transaction object * @param[in] event Event type * @param[in] cbdata Callback data (module) * * @returns Status code */ static ib_status_t modua_remoteip(ib_engine_t *ib, ib_tx_t *tx, ib_state_event_type_t event, void *cbdata) { assert(ib != NULL); assert(tx != NULL); assert(tx->var_store != NULL); assert(event == handle_context_tx_event); const ib_module_t *m = (const ib_module_t *)cbdata; ib_field_t *field = NULL; ib_status_t rc = IB_OK; const ib_bytestr_t *bs; const uint8_t *data; size_t len; char *buf; uint8_t *comma; const ib_list_t *list; const ib_list_node_t *node; const ib_field_t *forwarded; uint8_t *stripped; size_t num; ib_flags_t flags; const modua_config_t *cfg; rc = ib_context_module_config(ib_context_main(ib), m, &cfg); if (rc != IB_OK) { ib_log_error_tx(tx, "Can't fetch configuration: %s", ib_status_to_string(rc)); return rc; } ib_log_debug3_tx(tx, "Checking for alternate remote address"); /* Extract the X-Forwarded-For header field */ rc = ib_var_target_get_const( cfg->forwarded_for, &list, tx->mp, tx->var_store ); if (rc == IB_ENOENT || ib_list_elements(list) == 0) { ib_log_debug_tx(tx, "No X-Forwarded-For"); return IB_OK; } if (rc != IB_OK) { ib_log_error_tx(tx, "Cannot retrieve request_headers:User-Agent: %d", rc); return rc; } num = ib_list_elements(list); if (num == 0) { ib_log_debug_tx(tx, "No X-Forwarded-For header found"); return rc; } else if (num != 1) { ib_log_debug_tx(tx, "%zd X-Forwarded-For headers found: ignoring", num); return rc; } node = ib_list_last_const(list); if ( (node == NULL) || (node->data == NULL) ) { ib_log_notice_tx(tx, "Invalid X-Forwarded-For header found"); return rc; } forwarded = (const ib_field_t *)node->data; /* Found it: copy the data into a newly allocated string buffer */ rc = ib_field_value_type(forwarded, ib_ftype_bytestr_out(&bs), IB_FTYPE_BYTESTR); if (rc != IB_OK) { ib_log_notice_tx(tx, "Invalid X-Forwarded-For header value"); return rc; } if (bs == NULL) { ib_log_notice_tx(tx, "X-Forwarded-For header not a bytestr"); return IB_EINVAL; } len = ib_bytestr_length(bs); data = ib_bytestr_const_ptr(bs); /* Search for a comma in the buffer */ comma = memchr(data, ',', len); if (comma != NULL) { len = comma - data; } /* Trim whitespace */ stripped = (uint8_t *)data; rc = ib_strtrim_lr_ex(IB_STROP_INPLACE, tx->mp, stripped, len, &stripped, &len, &flags); if (rc != IB_OK) { return rc; } /* Verify that it looks like a valid IP v4/6 address */ rc = ib_ip_validate_ex((const char *)stripped, len); if (rc != IB_OK) { ib_log_error_tx(tx, "X-Forwarded-For \"%.*s\" is not a valid IP address", (int)len, stripped ); return IB_OK; } /* Allocate memory for copy of stripped string */ buf = (char *)ib_mpool_alloc(tx->mp, len+1); if (buf == NULL) { ib_log_error_tx(tx, "Failed to allocate %zd bytes for remote address", len+1); return IB_EALLOC; } /* Copy the string out */ memcpy(buf, stripped, len); buf[len] = '\0'; ib_log_debug_tx(tx, "Remote address changed to \"%s\"", buf); /* This will lose the pointer to the original address * buffer, but it should be cleaned up with the rest * of the memory pool. */ tx->er_ipstr = buf; /* Update the remote address field in the tx collection */ rc = ib_field_create_bytestr_alias( &field, tx->mp, "", 0, (uint8_t *)buf, len ); if (rc != IB_OK) { ib_log_error_tx(tx, "Failed to create field for remote_addr: %s", ib_status_to_string(rc)); return rc; } rc = ib_var_source_set(cfg->remote_addr, tx->var_store, field); if (rc != IB_OK) { ib_log_error_tx(tx, "Failed to set remote address var: %s", ib_status_to_string(rc)); return rc; } return IB_OK; }
/** * Handle request_header events for user agent extraction. * * Extract the "request_headers" field (a list) from the transactions's * data provider instance, then loop through the list, looking for the * "User-Agent" field. If found, the value is parsed and used to update the * connection object fields. * * @param[in] ib IronBee object * @param[in,out] tx Transaction. * @param[in] event Event type * @param[in] data Callback data (module) * * @returns Status code */ static ib_status_t modua_user_agent(ib_engine_t *ib, ib_tx_t *tx, ib_state_event_type_t event, void *data) { assert(ib != NULL); assert(tx != NULL); assert(tx->var_store != NULL); assert(event == handle_context_tx_event); assert(data != NULL); const ib_module_t *m = (const ib_module_t *)data; const ib_field_t *req_agent = NULL; ib_status_t rc = IB_OK; const ib_list_t *bs_list; const ib_bytestr_t *bs; const modua_config_t *cfg; rc = ib_context_module_config(ib_context_main(ib), m, &cfg); if (rc != IB_OK) { ib_log_error_tx(tx, "Can't fetch configuration: %s", ib_status_to_string(rc)); return rc; } /* Extract the User-Agent header field */ rc = ib_var_target_get_const( cfg->user_agent, &bs_list, tx->mp, tx->var_store ); if (rc == IB_ENOENT || ib_list_elements(bs_list) == 0) { ib_log_debug_tx(tx, "request_header_finished_event: No user agent"); return IB_OK; } if (rc != IB_OK) { ib_log_error_tx(tx, "Cannot retrieve request_headers:User-Agent: %d", rc); return rc; } if (IB_LIST_ELEMENTS(bs_list) == 0) { ib_log_debug_tx(tx, "handle_context_tx_event: No user agent"); return IB_OK; } req_agent = (ib_field_t *)IB_LIST_NODE_DATA(IB_LIST_LAST(bs_list)); /* Found it: copy the data into a newly allocated string buffer */ rc = ib_field_value_type(req_agent, ib_ftype_bytestr_out(&bs), IB_FTYPE_BYTESTR); if (rc != IB_OK) { ib_log_error_tx(tx, "Request user agent is not a BYTESTR: %s", ib_status_to_string(rc)); return rc; } /* Finally, split it up & store the components */ rc = modua_agent_fields(ib, tx, bs); return rc; }
/** * Feed a collection of byte strings from an @ref ib_data_t to automata. * * @param[in] ib IronBee engine; used for logging. * @param[in] eudoxus Eudoxus engine; used for ia_eudoxus_error(). * @param[in] state Current Eudoxus execution state; updated. * @param[in] data Data source. * @param[in] collection Collection to feed. * @return * - IB_OK on success. * - IB_EINVAL on IronAutomata failure; will emit log message. * - IB_EOTHER on IronBee failure; will emit log message. */ static ib_status_t fast_feed_data_collection( const ib_engine_t *ib, const ia_eudoxus_t *eudoxus, ia_eudoxus_state_t *state, const ib_data_t *data, const fast_collection_spec_t *collection ) { assert(ib != NULL); assert(eudoxus != NULL); assert(state != NULL); assert(data != NULL); assert(collection != NULL); ib_field_t *field; const ib_list_t *subfields; const ib_list_node_t *node; const ib_field_t *subfield; const ib_bytestr_t *bs; ib_status_t rc; rc = ib_data_get(data, collection->name, &field); if (rc == IB_ENOENT) { ib_log_error( ib, "fast: No such data %s", collection->name ); return IB_EOTHER; } else if (rc != IB_OK) { ib_log_error( ib, "fast: Error fetching data %s: %s", collection->name, ib_status_to_string(rc) ); return IB_EOTHER; } rc = ib_field_value_type( field, ib_ftype_list_out(&subfields), IB_FTYPE_LIST ); if (rc != IB_OK) { ib_log_error( ib, "fast: Error loading data field %s: %s", collection->name, ib_status_to_string(rc) ); return IB_EOTHER; } IB_LIST_LOOP_CONST(subfields, node) { subfield = (const ib_field_t *)ib_list_node_data_const(node); assert(subfield != NULL); rc = ib_field_value_type( subfield, ib_ftype_bytestr_out(&bs), IB_FTYPE_BYTESTR ); if (rc != IB_OK) { ib_log_error( ib, "fast: Error loading data subfield %s of %s: %s", subfield->name, collection->name, ib_status_to_string(rc) ); return IB_EOTHER; } rc = fast_feed( ib, eudoxus, state, (const uint8_t *)subfield->name, subfield->nlen ); if (rc != IB_OK) { return rc; } rc = fast_feed( ib, eudoxus, state, (const uint8_t *)collection->separator, strlen(collection->separator) ); if (rc != IB_OK) { return rc; } if (ib_bytestr_const_ptr(bs) != NULL && ib_bytestr_size(bs) > 0) { rc = fast_feed( ib, eudoxus, state, ib_bytestr_const_ptr(bs), ib_bytestr_size(bs) ); if (rc != IB_OK) { return rc; } } rc = fast_feed( ib, eudoxus, state, (const uint8_t *)c_data_separator, strlen(c_data_separator) ); if (rc != IB_OK) { return rc; } }
/** * Feed a byte string from an @ref ib_data_t to the automata. * * @param[in] ib IronBee engine; used for logging. * @param[in] eudoxus Eudoxus engine; used for * ia_eudoxus_error(). * @param[in] state Current Eudoxus execution state; updated. * @param[in] data Data source. * @param[in] bytestring_field_name Name of data field to feed. * @return * - IB_OK on success. * - IB_EINVAL on IronAutomata failure; will emit log message. * - IB_EOTHER on IronBee failure; will emit log message. */ static ib_status_t fast_feed_data_bytestring( const ib_engine_t *ib, const ia_eudoxus_t *eudoxus, ia_eudoxus_state_t *state, const ib_data_t *data, const char *bytestring_field_name ) { assert(ib != NULL); assert(eudoxus != NULL); assert(state != NULL); assert(data != NULL); assert(bytestring_field_name != NULL); ib_field_t *field; const ib_bytestr_t *bs; ib_status_t rc; rc = ib_data_get(data, bytestring_field_name, &field); if (rc == IB_ENOENT) { ib_log_error( ib, "fast: No such data %s", bytestring_field_name ); return IB_EOTHER; } else if (rc != IB_OK) { ib_log_error( ib, "fast: Error fetching data %s: %s", bytestring_field_name, ib_status_to_string(rc) ); return IB_EOTHER; } rc = ib_field_value_type( field, ib_ftype_bytestr_out(&bs), IB_FTYPE_BYTESTR ); if (rc != IB_OK) { ib_log_error( ib, "fast: Error loading data field %s: %s", bytestring_field_name, ib_status_to_string(rc) ); return IB_EOTHER; } if (ib_bytestr_const_ptr(bs) == NULL || ib_bytestr_size(bs) == 0) { return IB_OK; } return fast_feed( ib, eudoxus, state, ib_bytestr_const_ptr(bs), ib_bytestr_size(bs) ); }