From: Michael Altizer (mialtize) Date: Mon, 15 Oct 2018 21:22:46 +0000 (-0400) Subject: Merge pull request #1386 in SNORT/snort3 from cisco-wip to master X-Git-Tag: 3.0.0-249~30 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e29f105b89cf3fab978e37b6f97edbc7e77638ba;p=thirdparty%2Fsnort3.git Merge pull request #1386 in SNORT/snort3 from cisco-wip to master Squashed commit of the following: commit b30a30a659ca307a784bc47d41c815f19e505e2a Author: Bhagya Tholpady Date: Thu Jul 19 11:27:50 2018 -0400 detection/http_inspect: Save a snapshot HTTP buffers in the IPS context to support offload of HTTP flows --- diff --git a/src/detection/detection_engine.cc b/src/detection/detection_engine.cc index 44d764040..c8a6c210e 100644 --- a/src/detection/detection_engine.cc +++ b/src/detection/detection_engine.cc @@ -208,7 +208,12 @@ IpsContextData* DetectionEngine::get_data(unsigned id, IpsContext* context) if ( context ) return context->get_context_data(id); - return DetectionEngine::get_data(id); + ContextSwitcher* sw = Snort::get_switcher(); + + if ( !sw ) + return nullptr; + + return sw->get_context()->get_context_data(id); } void DetectionEngine::add_replacement(const std::string& s, unsigned off) diff --git a/src/service_inspectors/http_inspect/CMakeLists.txt b/src/service_inspectors/http_inspect/CMakeLists.txt index bd3a7001b..295065470 100644 --- a/src/service_inspectors/http_inspect/CMakeLists.txt +++ b/src/service_inspectors/http_inspect/CMakeLists.txt @@ -46,6 +46,8 @@ set (FILE_LIST http_test_input.h http_flow_data.cc http_flow_data.h + http_context_data.cc + http_context_data.h http_transaction.cc http_transaction.h http_test_manager.cc diff --git a/src/service_inspectors/http_inspect/http_api.cc b/src/service_inspectors/http_inspect/http_api.cc index 2c77c4cf6..bc5da95b1 100644 --- a/src/service_inspectors/http_inspect/http_api.cc +++ b/src/service_inspectors/http_inspect/http_api.cc @@ -23,6 +23,7 @@ #include "http_api.h" +#include "http_context_data.h" #include "http_inspect.h" using namespace snort; @@ -36,6 +37,12 @@ Inspector* HttpApi::http_ctor(Module* mod) return new HttpInspect(http_mod->get_once_params()); } +void HttpApi::http_init() +{ + HttpFlowData::init(); + HttpContextData::init(); +} + const char* HttpApi::classic_buffer_names[] = { "http_client_body", diff --git a/src/service_inspectors/http_inspect/http_api.h b/src/service_inspectors/http_inspect/http_api.h index 2116e0711..04d4ba97f 100644 --- a/src/service_inspectors/http_inspect/http_api.h +++ b/src/service_inspectors/http_inspect/http_api.h @@ -38,7 +38,7 @@ private: static void http_mod_dtor(snort::Module* m) { delete m; } static const char* http_my_name; static const char* http_help; - static void http_init() { HttpFlowData::init(); } + static void http_init(); static void http_term() { } static snort::Inspector* http_ctor(snort::Module* mod); static void http_dtor(snort::Inspector* p) { delete p; } diff --git a/src/service_inspectors/http_inspect/http_context_data.cc b/src/service_inspectors/http_inspect/http_context_data.cc new file mode 100644 index 000000000..8cde97e68 --- /dev/null +++ b/src/service_inspectors/http_inspect/http_context_data.cc @@ -0,0 +1,72 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// http_context_data.cc author Bhagya Tholpady + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "http_context_data.h" + +#include "http_msg_section.h" +#include "protocols/packet.h" + +using namespace snort; + +unsigned HttpContextData::ips_id = 0; + +HttpMsgSection* HttpContextData::get_snapshot(const Packet* p) +{ + IpsContext* context = p ? p->context : nullptr; + HttpContextData* hcd = (HttpContextData*)DetectionEngine::get_data(HttpContextData::ips_id, + context); + + if ( !hcd ) + return nullptr; + + return hcd->current_section; +} + +void HttpContextData::save_snapshot(HttpMsgSection* section) +{ + HttpContextData* hcd = (HttpContextData*)DetectionEngine::get_data(HttpContextData::ips_id); + + if ( !hcd ) + { + hcd = new HttpContextData; + DetectionEngine::set_data(HttpContextData::ips_id, hcd); + } + + hcd->current_section = section; + section->add_ips_context(DetectionEngine::get_context()); +} + +HttpMsgSection* HttpContextData::clear_snapshot(IpsContext* context) +{ + HttpMsgSection* section = nullptr; + HttpContextData* hcd = (HttpContextData*)DetectionEngine::get_data(HttpContextData::ips_id, + context); + + if ( hcd ) + { + section = hcd->current_section; + hcd->clear(); + } + + return section; +} diff --git a/src/service_inspectors/http_inspect/http_context_data.h b/src/service_inspectors/http_inspect/http_context_data.h new file mode 100644 index 000000000..9396d0eed --- /dev/null +++ b/src/service_inspectors/http_inspect/http_context_data.h @@ -0,0 +1,45 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2018-2018 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// http_context_data.h author Bhagya Tholpady + +#ifndef HTTP_CONTEXT_DATA_H +#define HTTP_CONTEXT_DATA_H + +#include "detection/ips_context.h" + +class HttpMsgSection; + +class HttpContextData : public snort::IpsContextData +{ +public: + void clear() override + { current_section = nullptr; } + + static void init() + { ips_id = IpsContextData::get_ips_id(); } + static HttpMsgSection* get_snapshot(const snort::Packet* p); + static void save_snapshot(HttpMsgSection* section); + static HttpMsgSection* clear_snapshot(snort::IpsContext* context); + static unsigned ips_id; + +private: + HttpMsgSection* current_section = nullptr; +}; + +#endif + diff --git a/src/service_inspectors/http_inspect/http_flow_data.cc b/src/service_inspectors/http_inspect/http_flow_data.cc index 610ea5e18..12edf371f 100644 --- a/src/service_inspectors/http_inspect/http_flow_data.cc +++ b/src/service_inspectors/http_inspect/http_flow_data.cc @@ -74,7 +74,7 @@ HttpFlowData::~HttpFlowData() delete infractions[k]; delete events[k]; delete[] section_buffer[k]; - HttpTransaction::delete_transaction(transaction[k]); + HttpTransaction::delete_transaction(transaction[k], nullptr); delete cutter[k]; if (compress_stream[k] != nullptr) { @@ -91,6 +91,13 @@ HttpFlowData::~HttpFlowData() if (fd_state != nullptr) File_Decomp_StopFree(fd_state); delete_pipeline(); + + while (discard_list != nullptr) + { + HttpTransaction* tmp = discard_list; + discard_list = discard_list->next; + delete tmp; + } } void HttpFlowData::half_reset(SourceId source_id) @@ -161,6 +168,22 @@ void HttpFlowData::trailer_prep(SourceId source_id) detection_status[source_id] = DET_REACTIVATING; } +void HttpFlowData::garbage_collect() +{ + HttpTransaction** current = &discard_list; + while (*current != nullptr) + { + if ((*current)->is_clear()) + { + HttpTransaction* tmp = *current; + *current = (*current)->next; + delete tmp; + } + else + current = &(*current)->next; + } +} + bool HttpFlowData::add_to_pipeline(HttpTransaction* latest) { if (pipeline == nullptr) @@ -195,7 +218,7 @@ void HttpFlowData::delete_pipeline() { for (int k=pipeline_front; k != pipeline_back; k = (k+1) % MAX_PIPELINE) { - HttpTransaction::delete_transaction(pipeline[k]); + delete pipeline[k]; } delete[] pipeline; } diff --git a/src/service_inspectors/http_inspect/http_flow_data.h b/src/service_inspectors/http_inspect/http_flow_data.h index 11011f229..2a3fc1390 100644 --- a/src/service_inspectors/http_inspect/http_flow_data.h +++ b/src/service_inspectors/http_inspect/http_flow_data.h @@ -67,6 +67,7 @@ private: // Convenience routines void half_reset(HttpEnums::SourceId source_id); void trailer_prep(HttpEnums::SourceId source_id); + void garbage_collect(); // 0 element refers to client request, 1 element refers to server response @@ -133,10 +134,10 @@ private: int64_t detect_depth_remaining[2] = { HttpEnums::STAT_NOT_PRESENT, HttpEnums::STAT_NOT_PRESENT }; uint64_t expected_trans_num[2] = { 1, 1 }; + // number of user data octets seen so far (regular body or chunks) int64_t body_octets[2] = { HttpEnums::STAT_NOT_PRESENT, HttpEnums::STAT_NOT_PRESENT }; int32_t status_code_num = HttpEnums::STAT_NOT_PRESENT; - HttpMsgSection* latest_section = nullptr; HttpEnums::VersionId version_id[2] = { HttpEnums::VERS__NOT_PRESENT, HttpEnums::VERS__NOT_PRESENT }; HttpEnums::MethodId method_id = HttpEnums::METH__NOT_PRESENT; @@ -154,6 +155,9 @@ private: HttpTransaction* take_from_pipeline(); void delete_pipeline(); + // Transactions with uncleared sections awaiting deletion + HttpTransaction* discard_list = nullptr; + #ifdef REG_TEST static uint64_t instance_count; uint64_t seq_num; diff --git a/src/service_inspectors/http_inspect/http_inspect.cc b/src/service_inspectors/http_inspect/http_inspect.cc index 2dcd91fd0..545a16141 100644 --- a/src/service_inspectors/http_inspect/http_inspect.cc +++ b/src/service_inspectors/http_inspect/http_inspect.cc @@ -29,6 +29,7 @@ #include "protocols/packet.h" #include "stream/stream.h" +#include "http_context_data.h" #include "http_js_norm.h" #include "http_msg_body.h" #include "http_msg_body_chunk.h" @@ -81,24 +82,22 @@ bool HttpInspect::configure(SnortConfig* ) InspectSection HttpInspect::get_latest_is(const Packet* p) { - const HttpFlowData* const session_data = - (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id); + HttpMsgSection* current_section = HttpContextData::get_snapshot(p); - if ((session_data == nullptr) || (session_data->latest_section == nullptr)) + if (current_section == nullptr) return HttpEnums::IS_NONE; - return session_data->latest_section->get_inspection_section(); + return current_section->get_inspection_section(); } SourceId HttpInspect::get_latest_src(const Packet* p) { - const HttpFlowData* const session_data = - (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id); + HttpMsgSection* current_section = HttpContextData::get_snapshot(p); - if ((session_data == nullptr) || (session_data->latest_section == nullptr)) + if (current_section == nullptr) return HttpEnums::SRC__NOT_COMPUTE; - return session_data->latest_section->get_source_id(); + return current_section->get_source_id(); } bool HttpInspect::get_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b) @@ -127,13 +126,12 @@ bool HttpInspect::get_buf(unsigned id, Packet* p, InspectionBuffer& b) bool HttpInspect::http_get_buf(unsigned id, uint64_t sub_id, uint64_t form, Packet* p, InspectionBuffer& b) { - const HttpFlowData* const session_data = - (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id); + HttpMsgSection* current_section = HttpContextData::get_snapshot(p); - if ((session_data == nullptr) || (session_data->latest_section == nullptr)) + if (current_section == nullptr) return false; - const Field& buffer = session_data->latest_section->get_classic_buffer(id, sub_id, form); + const Field& buffer = current_section->get_classic_buffer(id, sub_id, form); if (buffer.length() <= 0) return false; @@ -166,16 +164,14 @@ bool HttpInspect::get_fp_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBu return get_buf(ibt, p, b); } -int HttpInspect::get_xtra_trueip(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type) +int HttpInspect::get_xtra_trueip(Flow*, uint8_t** buf, uint32_t* len, uint32_t* type) { - const HttpFlowData* const session_data = - (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); + HttpMsgSection* current_section = HttpContextData::get_snapshot(nullptr); - if ((session_data == nullptr) || (session_data->latest_section == nullptr)) + if (current_section == nullptr) return 0; - const HttpTransaction* const transaction = session_data->latest_section->get_transaction(); - HttpMsgHeader* const req_header = transaction->get_header(SRC_CLIENT); + HttpMsgHeader* const req_header = current_section->get_header(SRC_CLIENT); if (req_header == nullptr) return 0; const Field& true_ip = req_header->get_true_ip_addr(); @@ -188,16 +184,14 @@ int HttpInspect::get_xtra_trueip(Flow* flow, uint8_t** buf, uint32_t* len, uint3 return 1; } -int HttpInspect::get_xtra_uri(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type) +int HttpInspect::get_xtra_uri(Flow*, uint8_t** buf, uint32_t* len, uint32_t* type) { - const HttpFlowData* const session_data = - (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); + HttpMsgSection* current_section = HttpContextData::get_snapshot(nullptr); - if ((session_data == nullptr) || (session_data->latest_section == nullptr)) + if (current_section == nullptr) return 0; - const HttpTransaction* const transaction = session_data->latest_section->get_transaction(); - HttpMsgRequest* const request = transaction->get_request(); + HttpMsgRequest* const request = current_section->get_request(); if (request == nullptr) return 0; const Field& uri = request->get_uri(); @@ -211,16 +205,14 @@ int HttpInspect::get_xtra_uri(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t return 1; } -int HttpInspect::get_xtra_host(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type) +int HttpInspect::get_xtra_host(Flow*, uint8_t** buf, uint32_t* len, uint32_t* type) { - const HttpFlowData* const session_data = - (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); + HttpMsgSection* current_section = HttpContextData::get_snapshot(nullptr); - if ((session_data == nullptr) || (session_data->latest_section == nullptr)) + if (current_section == nullptr) return 0; - const HttpTransaction* const transaction = session_data->latest_section->get_transaction(); - HttpMsgHeader* const req_header = transaction->get_header(SRC_CLIENT); + HttpMsgHeader* const req_header = current_section->get_header(SRC_CLIENT); if (req_header == nullptr) return 0; const Field& host = req_header->get_header_value_norm(HEAD_HOST); @@ -237,21 +229,19 @@ int HttpInspect::get_xtra_host(Flow* flow, uint8_t** buf, uint32_t* len, uint32_ // The name of this method reflects its legacy purpose. We actually return the normalized data // from a response message body which may include other forms of normalization in addition to // JavaScript normalization. But if you don't turn JavaScript normalization on you get nothing. -int HttpInspect::get_xtra_jsnorm(Flow* flow, uint8_t** buf, uint32_t* len, uint32_t* type) +int HttpInspect::get_xtra_jsnorm(Flow*, uint8_t** buf, uint32_t* len, uint32_t* type) { - const HttpFlowData* const session_data = - (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); + HttpMsgSection* current_section = HttpContextData::get_snapshot(nullptr); - if ((session_data == nullptr) || (session_data->latest_section == nullptr) || - (session_data->latest_section->get_source_id() != SRC_SERVER) || - !session_data->latest_section->get_params()->js_norm_param.normalize_javascript) + if ((current_section == nullptr) || + (current_section->get_source_id() != SRC_SERVER) || + !current_section->get_params()->js_norm_param.normalize_javascript) return 0; - const HttpTransaction* const transaction = session_data->latest_section->get_transaction(); - HttpMsgBody* const body = transaction->get_body(); + HttpMsgBody* const body = current_section->get_body(); if (body == nullptr) return 0; - assert((void*)body == (void*)session_data->latest_section); + assert((void*)body == (void*)current_section); const Field& detect_data = body->get_detect_data(); if (detect_data.length() <= 0) return 0; @@ -288,6 +278,7 @@ void HttpInspect::eval(Packet* p) { DetectionEngine::disable_content(p); } + #ifdef REG_TEST else { @@ -300,6 +291,15 @@ void HttpInspect::eval(Packet* p) } #endif + // If current transaction is complete then we are done with it. This is strictly a memory + // optimization not necessary for correct operation. + if ((source_id == SRC_SERVER) && (session_data->type_expected[SRC_SERVER] == SEC_STATUS) && + session_data->transaction[SRC_SERVER]->final_response()) + { + HttpTransaction::delete_transaction(session_data->transaction[SRC_SERVER], session_data); + session_data->transaction[SRC_SERVER] = nullptr; + } + // Whenever we process a packet we set these flags. If someone asks for an extra data // buffer the JIT code will figure out if we actually have it. SetExtraData(p, xtra_trueip_id); @@ -311,6 +311,7 @@ void HttpInspect::eval(Packet* p) bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const flow, SourceId source_id, bool buf_owner) const { + HttpMsgSection* current_section; HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id); assert(session_data != nullptr); @@ -319,31 +320,31 @@ bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const switch (session_data->section_type[source_id]) { case SEC_REQUEST: - session_data->latest_section = new HttpMsgRequest( + current_section = new HttpMsgRequest( data, dsize, session_data, source_id, buf_owner, flow, params); break; case SEC_STATUS: - session_data->latest_section = new HttpMsgStatus( + current_section = new HttpMsgStatus( data, dsize, session_data, source_id, buf_owner, flow, params); break; case SEC_HEADER: - session_data->latest_section = new HttpMsgHeader( + current_section = new HttpMsgHeader( data, dsize, session_data, source_id, buf_owner, flow, params); break; case SEC_BODY_CL: - session_data->latest_section = new HttpMsgBodyCl( + current_section = new HttpMsgBodyCl( data, dsize, session_data, source_id, buf_owner, flow, params); break; case SEC_BODY_OLD: - session_data->latest_section = new HttpMsgBodyOld( + current_section = new HttpMsgBodyOld( data, dsize, session_data, source_id, buf_owner, flow, params); break; case SEC_BODY_CHUNK: - session_data->latest_section = new HttpMsgBodyChunk( + current_section = new HttpMsgBodyChunk( data, dsize, session_data, source_id, buf_owner, flow, params); break; case SEC_TRAILER: - session_data->latest_section = new HttpMsgTrailer( + current_section = new HttpMsgTrailer( data, dsize, session_data, source_id, buf_owner, flow, params); break; default: @@ -355,14 +356,14 @@ bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const return false; } - session_data->latest_section->analyze(); - session_data->latest_section->gen_events(); - session_data->latest_section->update_flow(); + current_section->analyze(); + current_section->gen_events(); + current_section->update_flow(); #ifdef REG_TEST if (HttpTestManager::use_test_output()) { - session_data->latest_section->print_section(HttpTestManager::get_output_file()); + current_section->print_section(HttpTestManager::get_output_file()); fflush(HttpTestManager::get_output_file()); if (HttpTestManager::use_test_input()) { @@ -373,8 +374,8 @@ bool HttpInspect::process(const uint8_t* data, const uint16_t dsize, Flow* const } #endif - session_data->latest_section->publish(); - return session_data->latest_section->detection_required(); + current_section->publish(); + return current_section->detection_required(); } void HttpInspect::clear(Packet* p) @@ -384,12 +385,23 @@ void HttpInspect::clear(Packet* p) HttpFlowData* const session_data = (HttpFlowData*)p->flow->get_flow_data(HttpFlowData::inspector_id); - if (session_data == nullptr) + if ( session_data == nullptr ) + return; + + HttpMsgSection* current_section = HttpContextData::clear_snapshot(p->context); + + // FIXIT-M This test is necessary because sometimes we get extra clears + // Convert to assert when that gets fixed. + if ( current_section == nullptr ) return; - session_data->latest_section = nullptr; - const SourceId source_id = (p->is_from_client()) ? SRC_CLIENT : SRC_SERVER; + current_section->clear(); + HttpTransaction* current_transaction = current_section->get_transaction(); + + const SourceId source_id = current_section->get_source_id(); + //FIXIT-M This check may not apply to the transaction attached to the packet + //in case of offload. if (session_data->detection_status[source_id] == DET_DEACTIVATING) { if (source_id == SRC_CLIENT) @@ -403,20 +415,7 @@ void HttpInspect::clear(Packet* p) session_data->detection_status[source_id] = DET_OFF; } - if (session_data->transaction[source_id] == nullptr) - return; - - // If current transaction is complete then we are done with it and should reclaim the space - if ((source_id == SRC_SERVER) && (session_data->type_expected[SRC_SERVER] == SEC_STATUS) && - session_data->transaction[SRC_SERVER]->final_response()) - { - HttpTransaction::delete_transaction(session_data->transaction[SRC_SERVER]); - session_data->transaction[SRC_SERVER] = nullptr; - } - else - { - // Get rid of most recent body section if present - session_data->transaction[source_id]->set_body(nullptr); - } + current_transaction->garbage_collect(); + session_data->garbage_collect(); } diff --git a/src/service_inspectors/http_inspect/http_msg_body.cc b/src/service_inspectors/http_inspect/http_msg_body.cc index a654c4d6f..8bc383e64 100644 --- a/src/service_inspectors/http_inspect/http_msg_body.cc +++ b/src/service_inspectors/http_inspect/http_msg_body.cc @@ -40,6 +40,7 @@ HttpMsgBody::HttpMsgBody(const uint8_t* buffer, const uint16_t buf_size, detection_section((body_octets == 0) && (session_data->detect_depth_remaining[source_id] > 0)) { transaction->set_body(this); + get_related_sections(); } void HttpMsgBody::analyze() @@ -223,8 +224,6 @@ void HttpMsgBody::do_file_processing(Field& file_data) snort::FileFlows* file_flows = snort::FileFlows::get_file_flows(flow); const bool download = (source_id == SRC_SERVER); - HttpMsgRequest* request = transaction->get_request(); - size_t file_index = 0; if ((request != nullptr) and (request->get_http_uri() != nullptr)) diff --git a/src/service_inspectors/http_inspect/http_msg_body.h b/src/service_inspectors/http_inspect/http_msg_body.h index 8d52893d6..b884c28a3 100644 --- a/src/service_inspectors/http_inspect/http_msg_body.h +++ b/src/service_inspectors/http_inspect/http_msg_body.h @@ -34,6 +34,7 @@ public: HttpEnums::InspectSection get_inspection_section() const override { return detection_section ? HttpEnums::IS_DETECTION : HttpEnums::IS_BODY; } bool detection_required() const override; + HttpMsgBody* get_body() override { return this; } const Field& get_classic_client_body(); const Field& get_detect_data() { return detect_data; } static void fd_event_callback(void* context, int event); diff --git a/src/service_inspectors/http_inspect/http_msg_header.cc b/src/service_inspectors/http_inspect/http_msg_header.cc index 63190753b..e2ee8aa53 100644 --- a/src/service_inspectors/http_inspect/http_msg_header.cc +++ b/src/service_inspectors/http_inspect/http_msg_header.cc @@ -41,6 +41,7 @@ HttpMsgHeader::HttpMsgHeader(const uint8_t* buffer, const uint16_t buf_size, HttpMsgHeadShared(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_) { transaction->set_header(this, source_id); + get_related_sections(); } void HttpMsgHeader::publish() @@ -172,8 +173,8 @@ void HttpMsgHeader::update_flow() return; } - if ((source_id == SRC_SERVER) && (transaction->get_request() != nullptr) && - (transaction->get_request()->get_method_id() == METH_HEAD)) + if ((source_id == SRC_SERVER) && (request != nullptr) && + (request->get_method_id() == METH_HEAD)) { // No body allowed by RFC for response to HEAD method session_data->half_reset(SRC_SERVER); diff --git a/src/service_inspectors/http_inspect/http_msg_request.cc b/src/service_inspectors/http_inspect/http_msg_request.cc index 5a9c0049c..2d263e68f 100644 --- a/src/service_inspectors/http_inspect/http_msg_request.cc +++ b/src/service_inspectors/http_inspect/http_msg_request.cc @@ -33,6 +33,7 @@ HttpMsgRequest::HttpMsgRequest(const uint8_t* buffer, const uint16_t buf_size, HttpMsgStart(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_) { transaction->set_request(this); + get_related_sections(); } void HttpMsgRequest::parse_start_line() diff --git a/src/service_inspectors/http_inspect/http_msg_section.cc b/src/service_inspectors/http_inspect/http_msg_section.cc index e6ee69606..05caac502 100644 --- a/src/service_inspectors/http_inspect/http_msg_section.cc +++ b/src/service_inspectors/http_inspect/http_msg_section.cc @@ -23,6 +23,7 @@ #include "http_msg_section.h" +#include "http_context_data.h" #include "http_msg_body.h" #include "http_msg_head_shared.h" #include "http_msg_header.h" @@ -50,6 +51,15 @@ HttpMsgSection::HttpMsgSection(const uint8_t* buffer, const uint16_t buf_size, tcp_close(session_data->tcp_close[source_id]) { assert((source_id == SRC_CLIENT) || (source_id == SRC_SERVER)); + HttpContextData::save_snapshot(this); +} + +HttpMsgSection::~HttpMsgSection() +{ + // FIXIT-L This clear call is to handle premature deletion (before offload completes) + // of message sections due to session deletion + if ( ips_context ) + HttpContextData::clear_snapshot(ips_context); } bool HttpMsgSection::detection_required() const @@ -141,81 +151,71 @@ const Field& HttpMsgSection::get_classic_buffer(unsigned id, uint64_t sub_id, ui { if (source_id != SRC_CLIENT) return Field::FIELD_NULL; - HttpMsgBody* body = transaction->get_body(); - return (body != nullptr) ? body->get_classic_client_body() : Field::FIELD_NULL; + return (get_body() != nullptr) ? get_body()->get_classic_client_body() : Field::FIELD_NULL; } case HTTP_BUFFER_COOKIE: case HTTP_BUFFER_RAW_COOKIE: { - HttpMsgHeader* header = transaction->get_header(buffer_side); - if (header == nullptr) + if (header[buffer_side] == nullptr) return Field::FIELD_NULL; - return (id == HTTP_BUFFER_COOKIE) ? header->get_classic_norm_cookie() : - header->get_classic_raw_cookie(); + return (id == HTTP_BUFFER_COOKIE) ? header[buffer_side]->get_classic_norm_cookie() : + header[buffer_side]->get_classic_raw_cookie(); } case HTTP_BUFFER_HEADER: case HTTP_BUFFER_TRAILER: { // FIXIT-L Someday want to be able to return field name or raw field value - HttpMsgHeadShared* const header = (id == HTTP_BUFFER_HEADER) ? - (HttpMsgHeadShared*)transaction->get_header(buffer_side) : - (HttpMsgHeadShared*)transaction->get_trailer(buffer_side); - if (header == nullptr) + HttpMsgHeadShared* const head = (id == HTTP_BUFFER_HEADER) ? + (HttpMsgHeadShared*)header[buffer_side] : (HttpMsgHeadShared*)trailer[buffer_side]; + if (head == nullptr) return Field::FIELD_NULL; if (sub_id == 0) - return header->get_classic_norm_header(); - return header->get_header_value_norm((HeaderId)sub_id); + return head->get_classic_norm_header(); + return head->get_header_value_norm((HeaderId)sub_id); } case HTTP_BUFFER_METHOD: { - HttpMsgRequest* request = transaction->get_request(); return (request != nullptr) ? request->get_method() : Field::FIELD_NULL; } case HTTP_BUFFER_RAW_BODY: { - HttpMsgBody* body = transaction->get_body(); - return (body != nullptr) ? body->msg_text : Field::FIELD_NULL; + return (get_body() != nullptr) ? get_body()->msg_text : Field::FIELD_NULL; } case HTTP_BUFFER_RAW_HEADER: { - HttpMsgHeader* header = transaction->get_header(buffer_side); - return (header != nullptr) ? header->get_classic_raw_header() : Field::FIELD_NULL; + return (header[buffer_side] != nullptr) ? header[buffer_side]->get_classic_raw_header() : + Field::FIELD_NULL; } case HTTP_BUFFER_RAW_REQUEST: { - HttpMsgRequest* request = transaction->get_request(); return (request != nullptr) ? request->msg_text : Field::FIELD_NULL; } case HTTP_BUFFER_RAW_STATUS: { - HttpMsgStatus* status = transaction->get_status(); return (status != nullptr) ? status->msg_text : Field::FIELD_NULL; } case HTTP_BUFFER_RAW_TRAILER: { - HttpMsgTrailer* trailer = transaction->get_trailer(buffer_side); - return (trailer != nullptr) ? trailer->get_classic_raw_header() : Field::FIELD_NULL; + return (trailer[buffer_side] != nullptr) ? trailer[buffer_side]->get_classic_raw_header() : + Field::FIELD_NULL; } case HTTP_BUFFER_STAT_CODE: { - HttpMsgStatus* status = transaction->get_status(); return (status != nullptr) ? status->get_status_code() : Field::FIELD_NULL; } case HTTP_BUFFER_STAT_MSG: { - HttpMsgStatus* status = transaction->get_status(); return (status != nullptr) ? status->get_reason_phrase() : Field::FIELD_NULL; } case HTTP_BUFFER_TRUE_IP: { - HttpMsgHeader* header = transaction->get_header(SRC_CLIENT); - return (header != nullptr) ? header->get_true_ip() : Field::FIELD_NULL; + return (header[SRC_CLIENT] != nullptr) ? header[SRC_CLIENT]->get_true_ip() : + Field::FIELD_NULL; } case HTTP_BUFFER_URI: case HTTP_BUFFER_RAW_URI: { const bool raw = (id == HTTP_BUFFER_RAW_URI); - HttpMsgRequest* request = transaction->get_request(); if (request == nullptr) return Field::FIELD_NULL; if (sub_id == 0) @@ -244,7 +244,7 @@ const Field& HttpMsgSection::get_classic_buffer(unsigned id, uint64_t sub_id, ui case HTTP_BUFFER_VERSION: { HttpMsgStart* start = (buffer_side == SRC_CLIENT) ? - (HttpMsgStart*)transaction->get_request() : (HttpMsgStart*)transaction->get_status(); + (HttpMsgStart*)request : (HttpMsgStart*)status; return (start != nullptr) ? start->get_version() : Field::FIELD_NULL; } default: @@ -253,6 +253,25 @@ const Field& HttpMsgSection::get_classic_buffer(unsigned id, uint64_t sub_id, ui } } +void HttpMsgSection::get_related_sections() +{ + // When a message section is created these relationships become fixed so we make copies for + // future reference. + request = transaction->get_request(); + status = transaction->get_status(); + header[SRC_CLIENT] = transaction->get_header(SRC_CLIENT); + header[SRC_SERVER] = transaction->get_header(SRC_SERVER); + trailer[SRC_CLIENT] = transaction->get_trailer(SRC_CLIENT); + trailer[SRC_SERVER] = transaction->get_trailer(SRC_SERVER); +} + +void HttpMsgSection::clear() +{ + transaction->clear_section(); + cleared = true; + ips_context = nullptr; +} + #ifdef REG_TEST void HttpMsgSection::print_section_title(FILE* output, const char* title) const diff --git a/src/service_inspectors/http_inspect/http_msg_section.h b/src/service_inspectors/http_inspect/http_msg_section.h index 7d7ee692e..f14077f2c 100644 --- a/src/service_inspectors/http_inspect/http_msg_section.h +++ b/src/service_inspectors/http_inspect/http_msg_section.h @@ -34,7 +34,7 @@ class HttpMsgSection { public: - virtual ~HttpMsgSection() = default; + virtual ~HttpMsgSection(); virtual HttpEnums::InspectSection get_inspection_section() const { return HttpEnums::IS_NONE; } virtual bool detection_required() const; @@ -42,6 +42,15 @@ public: HttpTransaction* get_transaction() const { return transaction; } const HttpParaList* get_params() const { return params; } + HttpMsgRequest* get_request() const { return request; } + HttpMsgStatus* get_status() const { return status; } + HttpMsgHeader* get_header(HttpEnums::SourceId source_id) const { return header[source_id]; } + HttpMsgTrailer* get_trailer(HttpEnums::SourceId source_id) const + { return trailer[source_id]; } + virtual HttpMsgBody* get_body() { return nullptr; } + + void add_ips_context(snort::IpsContext* context) { ips_context = context; } + // Minimum necessary processing for every message virtual void analyze() = 0; @@ -63,6 +72,11 @@ public: // Publish an inspection event for other modules to consume. virtual void publish() { } + void clear(); + bool is_clear() { return cleared; } + + HttpMsgSection* next = nullptr; + #ifdef REG_TEST // Test tool prints all derived message parts virtual void print_section(FILE* output) = 0; @@ -73,6 +87,8 @@ protected: HttpEnums::SourceId source_id_, bool buf_owner, snort::Flow* flow_, const HttpParaList* params_); + void get_related_sections(); + const Field msg_text; HttpFlowData* const session_data; snort::Flow* const flow; @@ -85,6 +101,15 @@ protected: HttpEnums::MethodId method_id; const bool tcp_close; + snort::IpsContext* ips_context = nullptr; + // Pointers to related message sections in the same transaction + HttpMsgRequest* request; + HttpMsgStatus* status; + HttpMsgHeader* header[2]; + HttpMsgTrailer* trailer[2]; + + bool cleared = false; + // Convenience methods shared by multiple subclasses void add_infraction(int infraction); void create_event(int sid); diff --git a/src/service_inspectors/http_inspect/http_msg_status.cc b/src/service_inspectors/http_inspect/http_msg_status.cc index 3bb5a5bff..376b889fa 100644 --- a/src/service_inspectors/http_inspect/http_msg_status.cc +++ b/src/service_inspectors/http_inspect/http_msg_status.cc @@ -35,6 +35,7 @@ HttpMsgStatus::HttpMsgStatus(const uint8_t* buffer, const uint16_t buf_size, HttpMsgStart(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_) { transaction->set_status(this); + get_related_sections(); } void HttpMsgStatus::parse_start_line() @@ -148,7 +149,7 @@ void HttpMsgStatus::gen_events() } } - if (!transaction->get_request() && (trans_num == 1)) + if (!request && (trans_num == 1)) { if (flow->is_pdu_inorder(SSN_DIR_FROM_SERVER)) { @@ -162,8 +163,8 @@ void HttpMsgStatus::gen_events() { // Verify that 206 Partial Content is in response to a Range request. Unsolicited 206 // responses indicate content is being fragmented for no good reason. - HttpMsgHeader* const req_header = transaction->get_header(SRC_CLIENT); - if ((req_header != nullptr) && (req_header->get_header_count(HEAD_RANGE) == 0)) + if ((header[SRC_CLIENT] != nullptr) && + (header[SRC_CLIENT]->get_header_count(HEAD_RANGE) == 0)) { add_infraction(INF_206_WITHOUT_RANGE); create_event(EVENT_206_WITHOUT_RANGE); @@ -190,8 +191,8 @@ void HttpMsgStatus::update_flow() if (status_code_num == 100) { // Were we "Expect"-ing this? - HttpMsgHeader* const req_header = transaction->get_header(SRC_CLIENT); - if ((req_header != nullptr) && (req_header->get_header_count(HEAD_EXPECT) == 0)) + if ((header[SRC_CLIENT] != nullptr) && + (header[SRC_CLIENT]->get_header_count(HEAD_EXPECT) == 0)) { add_infraction(INF_UNEXPECTED_100_RESPONSE); create_event(EVENT_UNEXPECTED_100_RESPONSE); diff --git a/src/service_inspectors/http_inspect/http_msg_trailer.cc b/src/service_inspectors/http_inspect/http_msg_trailer.cc index d711acc99..908cf1b5a 100644 --- a/src/service_inspectors/http_inspect/http_msg_trailer.cc +++ b/src/service_inspectors/http_inspect/http_msg_trailer.cc @@ -33,6 +33,7 @@ HttpMsgTrailer::HttpMsgTrailer(const uint8_t* buffer, const uint16_t buf_size, HttpMsgHeadShared(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_) { transaction->set_trailer(this, source_id); + get_related_sections(); } void HttpMsgTrailer::gen_events() diff --git a/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc b/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc index 97fc984e9..25f760aee 100644 --- a/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc +++ b/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc @@ -155,8 +155,10 @@ StreamSplitter::Status HttpStreamSplitter::scan(Flow* flow, const uint8_t* data, type = SEC_BODY_OLD; prepare_flush(session_data, nullptr, SEC_STATUS, 14, 0, 0, false, 0, 14, true); my_inspector->process((const uint8_t*)"HTTP/0.9 200 .", 14, flow, SRC_SERVER, false); + session_data->transaction[SRC_SERVER]->clear_section(); prepare_flush(session_data, nullptr, SEC_HEADER, 0, 0, 0, false, 0, 0, true); my_inspector->process((const uint8_t*)"", 0, flow, SRC_SERVER, false); + session_data->transaction[SRC_SERVER]->clear_section(); } HttpCutter*& cutter = session_data->cutter[source_id]; diff --git a/src/service_inspectors/http_inspect/http_transaction.cc b/src/service_inspectors/http_inspect/http_transaction.cc index 905a78fee..62caa7cd7 100644 --- a/src/service_inspectors/http_inspect/http_transaction.cc +++ b/src/service_inspectors/http_inspect/http_transaction.cc @@ -33,6 +33,16 @@ using namespace HttpEnums; +static void delete_section_list(HttpMsgSection* section_list) +{ + while (section_list != nullptr) + { + HttpMsgSection* tmp = section_list; + section_list = section_list->next; + delete tmp; + } +} + HttpTransaction::~HttpTransaction() { delete request; @@ -44,7 +54,8 @@ HttpTransaction::~HttpTransaction() delete infractions[k]; delete events[k]; } - delete latest_body; + delete_section_list(body_list); + delete_section_list(discard_list); } HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_data, SourceId @@ -76,20 +87,20 @@ HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_da // needed it. Instead the two sides have been sharing the transaction. This is a // soft delete that eliminates our interest in this transaction without disturbing // the possibly ongoing response processing. - delete_transaction(session_data->transaction[SRC_CLIENT]); + delete_transaction(session_data->transaction[SRC_CLIENT], session_data); } else if ((session_data->pipeline_overflow) || (session_data->pipeline_underflow)) { // Pipelining previously broke down and both sides are processed separately from // now on. We just throw things away when we are done with them. - delete_transaction(session_data->transaction[SRC_CLIENT]); + delete_transaction(session_data->transaction[SRC_CLIENT], session_data); } else if (!session_data->add_to_pipeline(session_data->transaction[SRC_CLIENT])) { // The pipeline is full and just overflowed. *session_data->infractions[source_id] += INF_PIPELINE_OVERFLOW; session_data->events[source_id]->create_event(EVENT_PIPELINE_MAX); - delete_transaction(session_data->transaction[SRC_CLIENT]); + delete_transaction(session_data->transaction[SRC_CLIENT], session_data); } } session_data->transaction[SRC_CLIENT] = new HttpTransaction; @@ -111,12 +122,12 @@ HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_da session_data->transaction[SRC_SERVER]->second_response_expected) { session_data->transaction[SRC_SERVER]->second_response_expected = false; - delete session_data->transaction[SRC_SERVER]->status; + session_data->transaction[SRC_SERVER]->discard_section( + session_data->transaction[SRC_SERVER]->status); session_data->transaction[SRC_SERVER]->status = nullptr; - delete session_data->transaction[SRC_SERVER]->header[SRC_SERVER]; + session_data->transaction[SRC_SERVER]->discard_section( + session_data->transaction[SRC_SERVER]->header[SRC_SERVER]); session_data->transaction[SRC_SERVER]->header[SRC_SERVER] = nullptr; - delete session_data->transaction[SRC_SERVER]->trailer[SRC_SERVER]; - session_data->transaction[SRC_SERVER]->trailer[SRC_SERVER] = nullptr; } // Status section: delete the current transaction and get a new one from the pipeline. If the // pipeline is empty check for a request transaction and take it. If there is no transaction @@ -124,7 +135,7 @@ HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_da // response side. else if (session_data->section_type[source_id] == SEC_STATUS) { - delete_transaction(session_data->transaction[SRC_SERVER]); + delete_transaction(session_data->transaction[SRC_SERVER], session_data); if (session_data->pipeline_underflow) { // A previous underflow separated the two sides forever @@ -170,24 +181,64 @@ HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_da } assert(session_data->transaction[source_id] != nullptr); + session_data->transaction[source_id]->active_sections++; return session_data->transaction[source_id]; } -void HttpTransaction::delete_transaction(HttpTransaction* transaction) +void HttpTransaction::discard_section(HttpMsgSection* section) +{ + if (section != nullptr) + { + section->next = discard_list; + discard_list = section; + } +} + +void HttpTransaction::clear_section() +{ + assert(active_sections > 0); + active_sections--; +} + +void HttpTransaction::garbage_collect() +{ + HttpMsgSection** current = (HttpMsgSection**)&body_list; + while (*current != nullptr) + { + if ((*current)->is_clear()) + { + HttpMsgSection* tmp = *current; + *current = (*current)->next; + delete tmp; + } + else + current = &(*current)->next; + } +} + +void HttpTransaction::delete_transaction(HttpTransaction* transaction, HttpFlowData* session_data) { if (transaction != nullptr) { if (!transaction->shared_ownership) - delete transaction; + { + if ((transaction->active_sections > 0) && (session_data != nullptr)) + { + transaction->next = session_data->discard_list; + session_data->discard_list = transaction; + } + else + delete transaction; + } else transaction->shared_ownership = false; } } -void HttpTransaction::set_body(HttpMsgBody* latest_body_) +void HttpTransaction::set_body(HttpMsgBody* latest_body) { - delete latest_body; - latest_body = latest_body_; + latest_body->next = body_list; + body_list = latest_body; } HttpInfractions* HttpTransaction::get_infractions(HttpEnums::SourceId source_id) diff --git a/src/service_inspectors/http_inspect/http_transaction.h b/src/service_inspectors/http_inspect/http_transaction.h index c092267ff..241116b89 100644 --- a/src/service_inspectors/http_inspect/http_transaction.h +++ b/src/service_inspectors/http_inspect/http_transaction.h @@ -36,9 +36,10 @@ class HttpEventGen; class HttpTransaction { public: + ~HttpTransaction(); static HttpTransaction* attach_my_transaction(HttpFlowData* session_data, HttpEnums::SourceId source_id); - static void delete_transaction(HttpTransaction* transaction); + static void delete_transaction(HttpTransaction* transaction, HttpFlowData* session_data); HttpMsgRequest* get_request() const { return request; } void set_request(HttpMsgRequest* request_) { request = request_; } @@ -54,9 +55,7 @@ public: { return trailer[source_id]; } void set_trailer(HttpMsgTrailer* trailer_, HttpEnums::SourceId source_id) { trailer[source_id] = trailer_; } - - HttpMsgBody* get_body() const { return latest_body; } - void set_body(HttpMsgBody* latest_body_); + void set_body(HttpMsgBody* latest_body); HttpInfractions* get_infractions(HttpEnums::SourceId source_id); HttpEventGen* get_events(HttpEnums::SourceId source_id); @@ -64,15 +63,24 @@ public: void set_one_hundred_response(); bool final_response() const { return !second_response_expected; } + void clear_section(); + bool is_clear() const { return active_sections == 0; } + void garbage_collect(); + + HttpTransaction* next = nullptr; + private: HttpTransaction() = default; - ~HttpTransaction(); + void discard_section(HttpMsgSection* section); + + uint64_t active_sections = 0; HttpMsgRequest* request = nullptr; HttpMsgStatus* status = nullptr; HttpMsgHeader* header[2] = { nullptr, nullptr }; HttpMsgTrailer* trailer[2] = { nullptr, nullptr }; - HttpMsgBody* latest_body = nullptr; + HttpMsgBody* body_list = nullptr; + HttpMsgSection* discard_list = nullptr; HttpInfractions* infractions[2] = { nullptr, nullptr }; HttpEventGen* events[2] = { nullptr, nullptr };