/** * IronBee callback function to set an HTTP error status. * This will divert processing into an ErrorDocument for the status. * * @param[in] tx - IronBee transaction * @param[in] status - Status to set * @return OK, or Declined if called too late. NOTIMPL should never happen. */ static ib_status_t ib_error_callback(ib_tx_t *tx, int status, void *cbdata) { ngxib_req_ctx *ctx = tx->sctx; if (status >= 200 && status < 600) { if (ctx->status >= 200 && ctx->status < 600) { ib_log_notice_tx(tx, "Ignoring: status already set to %d", ctx->status); return IB_OK; } if (ctx->start_response) { ib_log_notice_tx(tx, "Too late to change status=%d", status); return IB_DECLINED; } ib_log_info_tx(tx, "Setting status: %d -> %d", ctx->status, status); ctx->status = status; return IB_OK; } return IB_ENOTIMPL; }
/** * 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) { assert(ib != NULL); assert(tx != NULL); assert(tx->data != 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->data, "request_headers:X-Forwarded-For", &field); if ( (field == NULL) || (rc != IB_OK) ) { ib_log_debug_tx(tx, "No X-Forwarded-For field"); return 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"); 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_data_add_bytestr(tx->data, "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)); return rc; } return IB_OK; }
ib_status_t ib_parsed_req_line_create(ib_tx_t *tx, ib_parsed_req_line_t **line, const char *raw, size_t raw_len, const char *method, size_t method_len, const char *uri, size_t uri_len, const char *protocol, size_t protocol_len) { ib_status_t rc = IB_OK; assert(tx != NULL); assert(tx->ib != NULL); assert(tx->mp != NULL); ib_parsed_req_line_t *line_tmp = ib_mpool_alloc(tx->mp, sizeof(*line_tmp)); if ( line_tmp == NULL ) { *line = NULL; return IB_EALLOC; } /* Record the components if available. If the components are * not available, but the raw line is, then it will be possible * to parse the components out later on. Otherwise, if there * is no component and no raw line, then set default values. */ if (method != NULL) { rc = ib_bytestr_dup_mem(&line_tmp->method, tx->mp, (const uint8_t *)method, method_len); if (rc != IB_OK) { return rc; } } else { rc = ib_bytestr_dup_mem(&line_tmp->method, tx->mp, (const uint8_t *)"", 0); if (rc != IB_OK) { return rc; } } if (uri != NULL) { rc = ib_bytestr_dup_mem(&line_tmp->uri, tx->mp, (const uint8_t *)uri, uri_len); if (rc != IB_OK) { return rc; } } else { rc = ib_bytestr_dup_mem(&line_tmp->uri, tx->mp, (const uint8_t *)"", 0); if (rc != IB_OK) { return rc; } } if (protocol != NULL) { rc = ib_bytestr_dup_mem(&line_tmp->protocol, tx->mp, (const uint8_t *)protocol, protocol_len); if (rc != IB_OK) { return rc; } } else { rc = ib_bytestr_dup_mem(&line_tmp->protocol, tx->mp, (const uint8_t *)"", 0); if (rc != IB_OK) { return rc; } } /* If no raw line is available, then create one. */ if (raw == NULL) { if (method_len + uri_len + protocol_len == 0) { ib_log_notice_tx(tx, "Unable to generate raw request line without line " "components - using zero length request line." ); rc = ib_bytestr_dup_mem(&line_tmp->raw, tx->mp, (const uint8_t *)"", 0); if (rc != IB_OK) { return rc; } } else { size_t raw_line_len; assert(method != NULL); assert(uri != NULL); /* Create a correctly sized bytestr and manually copy * the data into it. */ raw_line_len = method_len + 1 + uri_len + (protocol == NULL ? 0 : 1 + protocol_len); rc = ib_bytestr_create(&line_tmp->raw, tx->mp, raw_line_len); if (rc != IB_OK) { return rc; } ib_log_debug_tx(tx, "Generating raw request line from components " "(length %zd).", raw_line_len); ib_bytestr_append_mem(line_tmp->raw, (const uint8_t *)method, method_len); ib_bytestr_append_mem(line_tmp->raw, (const uint8_t *)" ", 1); ib_bytestr_append_mem(line_tmp->raw, (const uint8_t *)uri, uri_len); if (protocol != NULL) { ib_bytestr_append_mem(line_tmp->raw, (const uint8_t *)" ", 1); ib_bytestr_append_mem(line_tmp->raw, (const uint8_t *)protocol, protocol_len); } } } else { rc = ib_bytestr_dup_mem(&line_tmp->raw, tx->mp, (const uint8_t *)raw, raw_len); if (rc != IB_OK) { return rc; } /* Now, if all components are missing, then parse them out * from the raw line. If only some are missing, then * do not assume anything is parsable. * * NOTE: This is a strict HTTP parser and assumes single * space (0x20) component separators. Better is to * have the server parse the components properly. */ if ((method == NULL) && (uri == NULL) && (protocol == NULL)) { uint8_t *raw_end = (uint8_t *)(raw + raw_len) - 1; uint8_t *ptr = (uint8_t *)raw; const uint8_t *parsed_field = ptr; ib_log_debug_tx(tx, "Parsing raw request line into components."); /* Parse the method. */ while (ptr <= raw_end) { if (*ptr == ' ') { break; } ++ptr; } rc = ib_bytestr_dup_mem(&line_tmp->method, tx->mp, parsed_field, (ptr - parsed_field)); if (rc != IB_OK) { return rc; } /* Parse the uri. */ parsed_field = ++ptr; while (ptr <= raw_end) { if (*ptr == ' ') { break; } ++ptr; } rc = ib_bytestr_dup_mem(&line_tmp->uri, tx->mp, parsed_field, (ptr - parsed_field)); if (rc != IB_OK) { return rc; } /* Parse the protocol. */ parsed_field = ++ptr; if (parsed_field <= raw_end) { rc = ib_bytestr_dup_mem(&line_tmp->protocol, tx->mp, parsed_field, (raw_end - parsed_field) + 1); if (rc != IB_OK) { return rc; } } else { rc = ib_bytestr_dup_mem(&line_tmp->protocol, tx->mp, (const uint8_t *)"", 0); if (rc != IB_OK) { return rc; } } } } /* Commit back successfully created line. */ *line = line_tmp; return IB_OK; }
/** * 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 == request_header_finished_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; }
/* * Callback used to generate request header fields. */ static ib_status_t core_gen_request_header_fields(ib_engine_t *ib, ib_tx_t *tx, ib_state_event_type_t event, void *cbdata) { ib_field_t *f; ib_status_t rc; assert(ib != NULL); assert(tx != NULL); assert(event == request_header_finished_event); /** * Alias connection remote and server addresses */ rc = ib_data_get(tx->conn->data, "server_addr", &f); if (rc != IB_OK) { return rc; } rc = ib_data_add(tx->data, f); if (rc != IB_OK) { return rc; } rc = ib_data_get(tx->conn->data, "server_port", &f); if (rc != IB_OK) { return rc; } rc = ib_data_add(tx->data, f); if (rc != IB_OK) { return rc; } rc = ib_data_get(tx->conn->data, "remote_addr", &f); if (rc != IB_OK) { return rc; } rc = ib_data_add(tx->data, f); if (rc != IB_OK) { return rc; } rc = ib_data_get(tx->conn->data, "remote_port", &f); if (rc != IB_OK) { return rc; } rc = ib_data_add(tx->data, f); if (rc != IB_OK) { return rc; } core_gen_tx_numeric_field(tx, "conn_tx_count", tx->conn->tx_count); if (tx->request_line != NULL) { core_gen_tx_bytestr_alias_field(tx, "request_line", tx->request_line->raw); core_gen_tx_bytestr_alias_field(tx, "request_method", tx->request_line->method); core_gen_tx_bytestr_alias_field(tx, "request_uri_raw", tx->request_line->uri); core_gen_tx_bytestr_alias_field(tx, "request_protocol", tx->request_line->protocol); } /* Populate the ARGS collection. */ rc = ib_data_get(tx->data, "ARGS", &f); if (rc == IB_OK) { ib_field_t *param_list; /* Add request URI parameters to ARGS collection. */ rc = ib_data_get(tx->data, "request_uri_params", ¶m_list); if (rc == IB_OK) { ib_list_t *field_list; ib_list_node_t *node = NULL; rc = ib_field_mutable_value( param_list, ib_ftype_list_mutable_out(&field_list) ); if (rc != IB_OK) { return rc; } IB_LIST_LOOP(field_list, node) { ib_field_t *param = (ib_field_t *)ib_list_node_data(node); /* Add the field to the ARGS collection. */ rc = ib_field_list_add(f, param); if (rc != IB_OK) { ib_log_notice_tx(tx, "Failed to add parameter to " "ARGS collection: %s", ib_status_to_string(rc)); } }
/* * Callback used to generate request header fields. */ static ib_status_t core_gen_request_header_fields(ib_engine_t *ib, ib_tx_t *tx, ib_state_event_type_t event, void *cbdata) { ib_field_t *f; ib_status_t rc; ib_conn_t *conn = tx->conn; assert(ib != NULL); assert(tx != NULL); assert(event == handle_request_header_event); core_gen_tx_bytestr_alias2(tx, "server_addr", conn->local_ipstr, strlen(conn->local_ipstr)); core_gen_tx_numeric(tx, "server_port", conn->local_port); core_gen_tx_bytestr_alias2(tx, "remote_addr", conn->remote_ipstr, strlen(conn->remote_ipstr)); core_gen_tx_numeric(tx, "remote_port", conn->remote_port); core_gen_tx_numeric(tx, "conn_tx_count", tx->conn->tx_count); if (tx->request_line != NULL) { core_gen_tx_bytestr_alias(tx, "request_line", tx->request_line->raw); core_gen_tx_bytestr_alias(tx, "request_method", tx->request_line->method); core_gen_tx_bytestr_alias(tx, "request_uri_raw", tx->request_line->uri); core_gen_tx_bytestr_alias(tx, "request_protocol", tx->request_line->protocol); } /* Populate the ARGS collection. */ rc = core_slow_get(&f, tx, "ARGS"); if (rc == IB_OK) { ib_field_t *param_list; rc = core_slow_get(¶m_list, tx, "request_uri_params"); if (rc == IB_OK) { ib_list_t *field_list; ib_list_node_t *node = NULL; rc = ib_field_mutable_value( param_list, ib_ftype_list_mutable_out(&field_list) ); if (rc != IB_OK) { return rc; } IB_LIST_LOOP(field_list, node) { ib_field_t *param = (ib_field_t *)ib_list_node_data(node); /* Add the field to the ARGS collection. */ rc = ib_field_list_add(f, param); if (rc != IB_OK) { ib_log_notice_tx(tx, "Failed to add parameter to " "ARGS collection: %s", ib_status_to_string(rc)); } }