From: Juweria Ali Imran (jaliimra) Date: Tue, 30 Apr 2024 12:45:22 +0000 (+0000) Subject: Pull request #4252: stream_tcp: support for asymmetric normalization X-Git-Tag: 3.2.1.0~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6878e30708283230166a975823c099941950cc38;p=thirdparty%2Fsnort3.git Pull request #4252: stream_tcp: support for asymmetric normalization Merge in SNORT/snort3 from ~JALIIMRA/snort3:asymmetric_normalization to master Squashed commit of the following: commit 4c5c502b823a2f6d832a5fd39ca60bb33189234b Author: Juweria Ali Imran Date: Wed Apr 17 21:18:47 2024 +0000 stream_tcp: support for asymmetric normalization --- diff --git a/src/stream/stream.h b/src/stream/stream.h index 2c03e8762..28e257709 100644 --- a/src/stream/stream.h +++ b/src/stream/stream.h @@ -60,7 +60,7 @@ class StreamSplitter; // sequence must match enum StreamPolicy defines in tcp_defs.h #define TCP_POLICIES \ "first | last | linux | old_linux | bsd | macos | solaris | irix | " \ - "hpux11 | hpux10 | windows | win_2003 | vista | proxy" + "hpux11 | hpux10 | windows | win_2003 | vista | proxy | asymmetric" struct AlertInfo { diff --git a/src/stream/tcp/tcp_defs.h b/src/stream/tcp/tcp_defs.h index 6d8bd81da..c76e847b2 100644 --- a/src/stream/tcp/tcp_defs.h +++ b/src/stream/tcp/tcp_defs.h @@ -76,6 +76,7 @@ enum StreamPolicy : uint8_t OS_WINDOWS2K3, OS_VISTA, OS_PROXY, + MISSED_3WHS, OS_END_OF_LIST, OS_DEFAULT = OS_BSD }; diff --git a/src/stream/tcp/tcp_module.cc b/src/stream/tcp/tcp_module.cc index 1768712ca..183ece674 100644 --- a/src/stream/tcp/tcp_module.cc +++ b/src/stream/tcp/tcp_module.cc @@ -119,6 +119,7 @@ const PegInfo tcp_pegs[] = { CountType::SUM, "proxy_mode_flows", "number of flows set to proxy normalization policy" }, { CountType::SUM, "full_retransmits", "number of fully retransmitted segments" }, { CountType::SUM, "flush_on_asymmetric_flow", "number of flushes on asymmetric flows" }, + { CountType::SUM, "asymmetric_flows", "number of completed flows having one-way traffic only" }, { CountType::END, nullptr, nullptr } }; @@ -363,10 +364,12 @@ bool StreamTcpModule::set(const char*, Value& v, SnortConfig*) else config->flags |= STREAM_CONFIG_NO_ASYNC_REASSEMBLY; } + else if ( v.is("require_3whs") ) { config->hs_timeout = v.get_int32(); } + else if ( v.is("show_rebuilt_packets") ) { if ( v.get_bool() ) @@ -374,6 +377,7 @@ bool StreamTcpModule::set(const char*, Value& v, SnortConfig*) else config->flags &= ~STREAM_CONFIG_SHOW_PACKETS; } + else if ( v.is("track_only") ) { if ( v.get_bool() ) diff --git a/src/stream/tcp/tcp_module.h b/src/stream/tcp/tcp_module.h index 5faba4f64..95c87e2eb 100644 --- a/src/stream/tcp/tcp_module.h +++ b/src/stream/tcp/tcp_module.h @@ -119,6 +119,7 @@ struct TcpStats PegCount proxy_mode_flows; PegCount full_retransmits; PegCount flush_on_asymmetric_flow; + PegCount asymmetric_flows; }; extern THREAD_LOCAL struct TcpStats tcpStats; diff --git a/src/stream/tcp/tcp_normalizer.cc b/src/stream/tcp/tcp_normalizer.cc index da8e9ba15..814d0e842 100644 --- a/src/stream/tcp/tcp_normalizer.cc +++ b/src/stream/tcp/tcp_normalizer.cc @@ -37,34 +37,25 @@ using namespace snort; TcpNormalizer::NormStatus TcpNormalizer::apply_normalizations( TcpNormalizerState& tns, TcpSegmentDescriptor& tsd, uint32_t seq, bool stream_is_inorder) { - // if this is a midstream pickup then skip normalizations - if ( Stream::is_midstream(tsd.get_flow()) ) - return NORM_OK; - - // these normalizations can't be done if we missed setup. and - // window is zero in one direction until we've seen both sides. - if ( tsd.get_flow()->two_way_traffic() ) + // drop packet if sequence num is invalid + if ( !tns.tracker->is_segment_seq_valid(tsd) ) { - // drop packet if sequence num is invalid - if ( !tns.tracker->is_segment_seq_valid(tsd) ) - { - bool inline_mode = tsd.is_nap_policy_inline(); - tcpStats.invalid_seq_num++; - log_drop_reason(tns, tsd, inline_mode, "normalizer", "Normalizer: Sequence number is invalid\n"); - trim_win_payload(tns, tsd, 0, inline_mode); - return NORM_BAD_SEQ; - } + bool inline_mode = tsd.is_nap_policy_inline(); + tcpStats.invalid_seq_num++; + log_drop_reason(tns, tsd, inline_mode, "normalizer", "Normalizer: Sequence number is invalid\n"); + trim_win_payload(tns, tsd, 0, inline_mode); + return NORM_BAD_SEQ; + } - // trim to fit in listener's window and mss - log_drop_reason(tns, tsd, false, "normalizer", "Normalizer: Trimming payload to fit window size\n"); - trim_win_payload(tns, tsd, - (tns.tracker->r_win_base + tns.tracker->get_snd_wnd() - tns.tracker->rcv_nxt)); + // trim to fit in listener's window and mss + log_drop_reason(tns, tsd, false, "normalizer", "Normalizer: Trimming payload to fit window size\n"); + trim_win_payload(tns, tsd, + (tns.tracker->r_win_base + tns.tracker->get_snd_wnd() - tns.tracker->rcv_nxt)); - if ( tns.tracker->get_mss() ) - trim_mss_payload(tns, tsd, tns.tracker->get_mss()); + if ( tns.tracker->get_mss() ) + trim_mss_payload(tns, tsd, tns.tracker->get_mss()); - ecn_stripper(tns, tsd); - } + ecn_stripper(tns, tsd); if ( stream_is_inorder ) { diff --git a/src/stream/tcp/tcp_normalizer.h b/src/stream/tcp/tcp_normalizer.h index 68e6a75ac..7ddd568a0 100644 --- a/src/stream/tcp/tcp_normalizer.h +++ b/src/stream/tcp/tcp_normalizer.h @@ -66,6 +66,7 @@ public: virtual ~TcpNormalizer() = default; virtual void init(State&) { } + virtual void init(TcpNormalizer*) { } virtual NormStatus apply_normalizations( State&, TcpSegmentDescriptor&, uint32_t seq, bool stream_is_inorder); @@ -111,6 +112,7 @@ protected: virtual int handle_paws_no_timestamps(State&, TcpSegmentDescriptor&); std::string my_name; + TcpNormalizer* prev_norm = nullptr; }; #endif diff --git a/src/stream/tcp/tcp_normalizers.cc b/src/stream/tcp/tcp_normalizers.cc index 4baa058db..599a3cb11 100644 --- a/src/stream/tcp/tcp_normalizers.cc +++ b/src/stream/tcp/tcp_normalizers.cc @@ -207,6 +207,22 @@ public: int handle_repeated_syn(TcpNormalizerState&, TcpSegmentDescriptor&) override; }; +// Normalizations applied or excluded for midstream and one-way asymmetric flows are common +class TcpNormalizerMissed3whs : public TcpNormalizer +{ +public: + TcpNormalizerMissed3whs() + { my_name = "Missed3whs"; } + + void init(TcpNormalizer* prev) override + { prev_norm = prev; } + + TcpNormalizer::NormStatus apply_normalizations( + TcpNormalizerState&, TcpSegmentDescriptor&, uint32_t seq, bool stream_is_inorder) override; + bool validate_rst(TcpNormalizerState&, TcpSegmentDescriptor&) override; + int handle_paws(TcpNormalizerState&, TcpSegmentDescriptor&) override; + int handle_repeated_syn(TcpNormalizerState&, TcpSegmentDescriptor&) override; +}; static inline int handle_repeated_syn_mswin( TcpStreamTracker* talker, TcpStreamTracker* listener, @@ -471,8 +487,43 @@ int TcpNormalizerProxy::handle_repeated_syn( return ACTION_NOTHING; } +TcpNormalizer::NormStatus TcpNormalizerMissed3whs::apply_normalizations( + TcpNormalizerState&, TcpSegmentDescriptor&, uint32_t, bool) +{ + // when a flow is Midstream/Asymmetric, not all packet normalizations are possible + return NORM_OK; +} + +bool TcpNormalizerMissed3whs::validate_rst( + TcpNormalizerState& tns, TcpSegmentDescriptor& tsd) +{ + if ( tns.session->flow->two_way_traffic() ) + return prev_norm->validate_rst(tns, tsd); + + if ( !prev_norm->get_name().compare("OS_Hpux11") ) + return validate_rst_seq_geq(tns, tsd); + + return true; +} + +int TcpNormalizerMissed3whs::handle_paws( + TcpNormalizerState& tns, TcpSegmentDescriptor& tsd) +{ + return ACTION_NOTHING; +} + +int TcpNormalizerMissed3whs::handle_repeated_syn( + TcpNormalizerState& tns, TcpSegmentDescriptor& tsd) +{ + return prev_norm->handle_repeated_syn(tns, tsd); +} + void TcpNormalizerPolicy::init(StreamPolicy os, TcpStreamSession* ssn, TcpStreamTracker* trk, TcpStreamTracker* peer) { + TcpNormalizer* prev_norm = nullptr; + if ( os == StreamPolicy::MISSED_3WHS and os != tns.os_policy ) + prev_norm = TcpNormalizerFactory::get_instance(tns.os_policy); + tns.os_policy = os; tns.session = ssn; tns.tracker = trk; @@ -492,7 +543,11 @@ void TcpNormalizerPolicy::init(StreamPolicy os, TcpStreamSession* ssn, TcpStream tns.opt_block = Normalize_GetMode(NORM_TCP_OPT); norm = TcpNormalizerFactory::get_instance(os); - norm->init(tns); + + if ( prev_norm ) + norm->init(prev_norm); + else + norm->init(tns); } TcpNormalizer* TcpNormalizerFactory::normalizers[StreamPolicy::OS_END_OF_LIST]; @@ -513,17 +568,18 @@ void TcpNormalizerFactory::initialize() normalizers[StreamPolicy::OS_WINDOWS2K3] = new TcpNormalizerWindows2K3; normalizers[StreamPolicy::OS_VISTA] = new TcpNormalizerVista; normalizers[StreamPolicy::OS_PROXY] = new TcpNormalizerProxy; + normalizers[StreamPolicy::MISSED_3WHS] = new TcpNormalizerMissed3whs; } void TcpNormalizerFactory::term() { - for ( auto sp = StreamPolicy::OS_FIRST; sp <= StreamPolicy::OS_PROXY; sp++ ) + for ( auto sp = StreamPolicy::OS_FIRST; sp < StreamPolicy::OS_END_OF_LIST; sp++ ) delete normalizers[sp]; } TcpNormalizer* TcpNormalizerFactory::get_instance(StreamPolicy sp) { - assert( sp <= StreamPolicy::OS_PROXY ); + assert( sp < StreamPolicy::OS_END_OF_LIST ); return normalizers[sp]; } diff --git a/src/stream/tcp/tcp_session.cc b/src/stream/tcp/tcp_session.cc index 4d53fd46e..60a3201c6 100644 --- a/src/stream/tcp/tcp_session.cc +++ b/src/stream/tcp/tcp_session.cc @@ -178,6 +178,9 @@ void TcpSession::clear_session(bool free_flow_data, bool flush_segments, bool re tcp_init = false; tcpStats.released++; + if ( !flow->two_way_traffic() and free_flow_data ) + tcpStats.asymmetric_flows++; + if ( flush_segments ) { client.reassembler.flush_queued_segments(flow, true, p); @@ -304,6 +307,19 @@ void TcpSession::update_perf_base_state(char newState) DataBus::publish(intrinsic_pub_id, IntrinsicEventIds::FLOW_STATE_CHANGE, nullptr, flow); } +void TcpSession::check_flow_missed_3whs() +{ + if ( flow->two_way_traffic() ) + return; + + if ( PacketTracer::is_active() ) + PacketTracer::log("Stream TCP did not see the complete 3-Way Handshake. " + "Not all normalizations will be in effect\n"); + + client.normalizer.init(StreamPolicy::MISSED_3WHS, this, &client, &server); + server.normalizer.init(StreamPolicy::MISSED_3WHS, this, &server, &client); +} + void TcpSession::update_stream_order(const TcpSegmentDescriptor& tsd, bool aligned) { TcpStreamTracker* listener = tsd.get_listener(); @@ -626,6 +642,9 @@ void TcpSession::check_for_session_hijack(TcpSegmentDescriptor& tsd) bool TcpSession::check_for_window_slam(TcpSegmentDescriptor& tsd) { + if (Stream::is_midstream(tsd.get_flow()) or !flow->two_way_traffic()) + return false; + TcpStreamTracker* listener = tsd.get_listener(); if ( tcp_config->max_window && (tsd.get_wnd() > tcp_config->max_window) ) @@ -638,8 +657,7 @@ bool TcpSession::check_for_window_slam(TcpSegmentDescriptor& tsd) } else if ( tsd.is_packet_from_client() && (tsd.get_wnd() <= SLAM_MAX) && (tsd.get_ack() == listener->get_iss() + 1) - && !(tsd.get_tcph()->is_fin() || tsd.get_tcph()->is_rst()) - && !(flow->get_session_flags() & SSNFLAG_MIDSTREAM)) + && !(tsd.get_tcph()->is_fin() || tsd.get_tcph()->is_rst())) { /* got a window slam alert! */ tel.set_tcp_event(EVENT_WINDOW_SLAM); @@ -1061,6 +1079,13 @@ void TcpSession::init_tcp_packet_analysis(TcpSegmentDescriptor& tsd) client.init_flush_policy(); server.init_flush_policy(); + if ( tsd.is_packet_from_client() ) // Important if the 3-way handshake's ACK contains data + flow->set_session_flags(SSNFLAG_SEEN_CLIENT); + else + flow->set_session_flags(SSNFLAG_SEEN_SERVER); + + check_flow_missed_3whs(); + set_no_ack(tcp_config->no_ack); } diff --git a/src/stream/tcp/tcp_session.h b/src/stream/tcp/tcp_session.h index cdae05701..8cd7ce9fe 100644 --- a/src/stream/tcp/tcp_session.h +++ b/src/stream/tcp/tcp_session.h @@ -90,6 +90,7 @@ private: bool filter_packet_for_reassembly(TcpSegmentDescriptor&, TcpStreamTracker*); void check_small_segment_threshold(const TcpSegmentDescriptor&, TcpStreamTracker*); int32_t kickstart_asymmetric_flow(const TcpSegmentDescriptor&, TcpStreamTracker*); + void check_flow_missed_3whs(); private: TcpStateMachine* tsm; diff --git a/src/stream/tcp/tcp_state_listen.cc b/src/stream/tcp/tcp_state_listen.cc index 0e0488ab3..12b7efc53 100644 --- a/src/stream/tcp/tcp_state_listen.cc +++ b/src/stream/tcp/tcp_state_listen.cc @@ -25,6 +25,7 @@ #include "tcp_state_listen.h" +#include "packet_tracer/packet_tracer.h" #include "pub_sub/stream_event_ids.h" #include "stream/stream.h" @@ -79,11 +80,21 @@ bool TcpStateListen::data_seg_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& if ( trk.session->is_midstream_allowed(tsd) ) { Flow* flow = tsd.get_flow(); - flow->session_state |= STREAM_STATE_MIDSTREAM; + if ( !Stream::is_midstream(flow) ) { + TcpStreamTracker* listener = tsd.get_listener(); + TcpStreamTracker* talker = tsd.get_talker(); + + trk.normalizer.init(StreamPolicy::MISSED_3WHS, trk.session, listener, talker); + trk.normalizer.init(StreamPolicy::MISSED_3WHS, trk.session, talker, listener); flow->set_session_flags(SSNFLAG_MIDSTREAM); + + if ( PacketTracer::is_active() ) + PacketTracer::log("Stream TCP did not see the complete 3-Way Handshake. " + "Not all normalizations will be in effect\n"); + DataBus::publish(Stream::get_pub_id(), StreamEventIds::TCP_MIDSTREAM, tsd.get_pkt()); } diff --git a/src/stream/tcp/tcp_state_none.cc b/src/stream/tcp/tcp_state_none.cc index 23bffd828..55727ac3c 100644 --- a/src/stream/tcp/tcp_state_none.cc +++ b/src/stream/tcp/tcp_state_none.cc @@ -25,6 +25,7 @@ #include "tcp_state_none.h" +#include "packet_tracer/packet_tracer.h" #include "pub_sub/stream_event_ids.h" #include "stream/stream.h" @@ -86,9 +87,20 @@ bool TcpStateNone::data_seg_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& tr { Flow* flow = tsd.get_flow(); flow->session_state |= STREAM_STATE_MIDSTREAM; + if ( !Stream::is_midstream(flow) ) { + TcpStreamTracker* listener = tsd.get_listener(); + TcpStreamTracker* talker = tsd.get_talker(); + + trk.normalizer.init(StreamPolicy::MISSED_3WHS, trk.session, listener, talker); + trk.normalizer.init(StreamPolicy::MISSED_3WHS, trk.session, talker, listener); flow->set_session_flags(SSNFLAG_MIDSTREAM); + + if ( PacketTracer::is_active() ) + PacketTracer::log("Stream TCP did not see the complete 3-Way Handshake. " + "Not all normalizations will be in effect\n"); + DataBus::publish(Stream::get_pub_id(), StreamEventIds::TCP_MIDSTREAM, tsd.get_pkt()); }