From 2ff6cbe888e7c74fe967a6b499f2545a8f4befa6 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 1 Aug 2023 20:50:17 +0200 Subject: [PATCH 1/6] sip/parser: accept valid chars Accepts valid characters as defined in RFC3261. --- rust/src/sip/parser.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/src/sip/parser.rs b/rust/src/sip/parser.rs index a34bc2615e53..9b813d5ace7d 100644 --- a/rust/src/sip/parser.rs +++ b/rust/src/sip/parser.rs @@ -15,7 +15,7 @@ * 02110-1301, USA. */ -// written by Giuseppe Longo +// written by Giuseppe Longo use nom7::bytes::streaming::{take, take_while, take_while1}; use nom7::character::streaming::{char, crlf}; @@ -59,7 +59,7 @@ pub struct Response { #[inline] fn is_token_char(b: u8) -> bool { - is_alphanumeric(b) || b"!%'*+-._`".contains(&b) + is_alphanumeric(b) || b"!%'*+-._`~".contains(&b) } #[inline] @@ -69,7 +69,7 @@ fn is_method_char(b: u8) -> bool { #[inline] fn is_request_uri_char(b: u8) -> bool { - is_alphanumeric(b) || is_token_char(b) || b"~#@:".contains(&b) + is_alphanumeric(b) || is_token_char(b) || b"~#@:;=?+&$,/".contains(&b) } #[inline] From 34bdcfabceef73165d2536cdbe13221657e50ac5 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 1 Aug 2023 21:08:50 +0200 Subject: [PATCH 2/6] rust/sip: register parser for sip This patch lets the parser to work over tcp protocol, taking care of handling data before calling the request/response parsers. --- rust/src/sip/sip.rs | 233 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 218 insertions(+), 15 deletions(-) diff --git a/rust/src/sip/sip.rs b/rust/src/sip/sip.rs index 4e86f5ea476d..97fa67057aea 100755 --- a/rust/src/sip/sip.rs +++ b/rust/src/sip/sip.rs @@ -20,7 +20,7 @@ use crate::frames::*; use crate::applayer::{self, *}; use crate::core; -use crate::core::{AppProto, Flow, ALPROTO_UNKNOWN}; +use crate::core::{AppProto, Flow, ALPROTO_UNKNOWN, ALPROTO_FAILED}; use crate::sip::parser::*; use nom7::Err; use std; @@ -112,6 +112,15 @@ impl SIPState { } } + fn append_request(&mut self, input: &[u8], request: Request) { + let mut tx = self.new_tx(crate::core::Direction::ToServer); + tx.request = Some(request); + if let Ok((_, req_line)) = sip_take_line(input) { + tx.request_line = req_line; + } + self.transactions.push(tx); + } + // app-layer-frame-documentation tag start: parse_request fn parse_request(&mut self, flow: *const core::Flow, stream_slice: StreamSlice) -> bool { let input = stream_slice.as_slice(); @@ -127,12 +136,7 @@ impl SIPState { match sip_parse_request(input) { Ok((_, request)) => { sip_frames_ts(flow, &stream_slice, &request); - let mut tx = self.new_tx(crate::core::Direction::ToServer); - tx.request = Some(request); - if let Ok((_, req_line)) = sip_take_line(input) { - tx.request_line = req_line; - } - self.transactions.push(tx); + self.append_request(input, request); return true; } // app-layer-frame-documentation tag end: parse_request @@ -147,6 +151,58 @@ impl SIPState { } } + fn parse_request_tcp(&mut self, flow: *const core::Flow, stream_slice: StreamSlice) -> AppLayerResult { + let input = stream_slice.as_slice(); + if input.is_empty() { + return AppLayerResult::ok(); + } + let _pdu = Frame::new( + flow, + &stream_slice, + input, + input.len() as i64, + SIPFrameType::Pdu as u8, + ); + SCLogDebug!("ts: pdu {:?}", _pdu); + + let mut start = input; + while !start.is_empty() { + match sip_parse_request(input) { + Ok((rem, request)) => { + start = rem; + sip_frames_ts(flow, &stream_slice, &request); + self.append_request(input, request); + } + Err(Err::Incomplete(_needed)) => { + let consumed = input.len() - start.len(); + let needed_estimation = start.len() + 1; + SCLogDebug!( + "Needed: {:?}, estimated needed: {:?}", + _needed, + needed_estimation + ); + return AppLayerResult::incomplete(consumed as u32, needed_estimation as u32); + } + Err(_) => { + self.set_event(SIPEvent::InvalidData); + return AppLayerResult::err(); + } + } + } + + // input fully consumed. + return AppLayerResult::ok(); + } + + fn append_response(&mut self, input: &[u8], response: Response) { + let mut tx = self.new_tx(crate::core::Direction::ToClient); + tx.response = Some(response); + if let Ok((_, resp_line)) = sip_take_line(input) { + tx.response_line = resp_line; + } + self.transactions.push(tx); + } + fn parse_response(&mut self, flow: *const core::Flow, stream_slice: StreamSlice) -> bool { let input = stream_slice.as_slice(); let _pdu = Frame::new(flow, &stream_slice, input, input.len() as i64, SIPFrameType::Pdu as u8); @@ -155,12 +211,7 @@ impl SIPState { match sip_parse_response(input) { Ok((_, response)) => { sip_frames_tc(flow, &stream_slice, &response); - let mut tx = self.new_tx(crate::core::Direction::ToClient); - tx.response = Some(response); - if let Ok((_, resp_line)) = sip_take_line(input) { - tx.response_line = resp_line; - } - self.transactions.push(tx); + self.append_response(input, response); return true; } Err(Err::Incomplete(_)) => { @@ -173,6 +224,49 @@ impl SIPState { } } } + + fn parse_response_tcp(&mut self, flow: *const core::Flow, stream_slice: StreamSlice) -> AppLayerResult { + let input = stream_slice.as_slice(); + if input.is_empty() { + return AppLayerResult::ok(); + } + let _pdu = Frame::new( + flow, + &stream_slice, + input, + input.len() as i64, + SIPFrameType::Pdu as u8, + ); + SCLogDebug!("tc: pdu {:?}", _pdu); + + let mut start = input; + while !start.is_empty() { + match sip_parse_response(input) { + Ok((rem, response)) => { + start = rem; + sip_frames_tc(flow, &stream_slice, &response); + self.append_response(input, response); + } + Err(Err::Incomplete(_needed)) => { + let consumed = input.len() - start.len(); + let needed_estimation = start.len() + 1; + SCLogDebug!( + "Needed: {:?}, estimated needed: {:?}", + _needed, + needed_estimation + ); + return AppLayerResult::incomplete(consumed as u32, needed_estimation as u32); + } + Err(_) => { + self.set_event(SIPEvent::InvalidData); + return AppLayerResult::err(); + } + } + } + + // input fully consumed. + return AppLayerResult::ok(); + } } impl SIPTransaction { @@ -298,6 +392,31 @@ pub unsafe extern "C" fn rs_sip_probing_parser_ts( return ALPROTO_UNKNOWN; } +#[no_mangle] +pub unsafe extern "C" fn rs_sip_probing_parser_tcp_ts( + _flow: *const Flow, + _direction: u8, + input: *const u8, + input_len: u32, + _rdir: *mut u8, +) -> AppProto { + if input_len >= 3 && !input.is_null() { + let buf = build_slice!(input, input_len as usize); + match sip_parse_request(buf) { + Ok((_, _request)) => { + return ALPROTO_SIP; + } + Err(Err::Incomplete(_)) => { + return ALPROTO_UNKNOWN; + } + Err(_e) => { + return ALPROTO_FAILED; + } + } + } + return ALPROTO_UNKNOWN; +} + #[no_mangle] pub unsafe extern "C" fn rs_sip_probing_parser_tc( _flow: *const Flow, @@ -313,6 +432,31 @@ pub unsafe extern "C" fn rs_sip_probing_parser_tc( return ALPROTO_UNKNOWN; } +#[no_mangle] +pub unsafe extern "C" fn rs_sip_probing_parser_tcp_tc( + _flow: *const Flow, + _direction: u8, + input: *const u8, + input_len: u32, + _rdir: *mut u8, +) -> AppProto { + if input_len >= 3 && !input.is_null() { + let buf = build_slice!(input, input_len as usize); + match sip_parse_response(buf) { + Ok((_, _response)) => { + return ALPROTO_SIP; + } + Err(Err::Incomplete(_)) => { + return ALPROTO_UNKNOWN; + } + Err(_e) => { + return ALPROTO_FAILED; + } + } + } + return ALPROTO_UNKNOWN; +} + #[no_mangle] pub unsafe extern "C" fn rs_sip_parse_request( flow: *const core::Flow, @@ -325,18 +469,58 @@ pub unsafe extern "C" fn rs_sip_parse_request( state.parse_request(flow, stream_slice).into() } +#[no_mangle] +pub unsafe extern "C" fn rs_sip_parse_request_tcp( + flow: *const core::Flow, + state: *mut std::os::raw::c_void, + pstate: *mut std::os::raw::c_void, + stream_slice: StreamSlice, + _data: *const std::os::raw::c_void, +) -> AppLayerResult { + if stream_slice.is_empty() { + if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) > 0 { + return AppLayerResult::ok(); + } else { + return AppLayerResult::err(); + } + } + + let state = cast_pointer!(state, SIPState); + state.parse_request_tcp(flow, stream_slice) +} + #[no_mangle] pub unsafe extern "C" fn rs_sip_parse_response( flow: *const core::Flow, state: *mut std::os::raw::c_void, - _pstate: *mut std::os::raw::c_void, + pstate: *mut std::os::raw::c_void, stream_slice: StreamSlice, _data: *const std::os::raw::c_void, ) -> AppLayerResult { + if stream_slice.is_empty() { + if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) > 0 { + return AppLayerResult::ok(); + } else { + return AppLayerResult::err(); + } + } + let state = cast_pointer!(state, SIPState); state.parse_response(flow, stream_slice).into() } +#[no_mangle] +pub unsafe extern "C" fn rs_sip_parse_response_tcp( + flow: *const core::Flow, + state: *mut std::os::raw::c_void, + _pstate: *mut std::os::raw::c_void, + stream_slice: StreamSlice, + _data: *const std::os::raw::c_void, +) -> AppLayerResult { + let state = cast_pointer!(state, SIPState); + state.parse_response_tcp(flow, stream_slice) +} + export_tx_data_get!(rs_sip_get_tx_data, SIPTransaction); export_state_data_get!(rs_sip_get_state_data, SIPState); @@ -345,7 +529,7 @@ const PARSER_NAME: &[u8] = b"sip\0"; #[no_mangle] pub unsafe extern "C" fn rs_sip_register_parser() { let default_port = CString::new("[5060,5061]").unwrap(); - let parser = RustParser { + let mut parser = RustParser { name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, default_port: default_port.as_ptr(), ipproto: core::IPPROTO_UDP, @@ -388,4 +572,23 @@ pub unsafe extern "C" fn rs_sip_register_parser() { } else { SCLogDebug!("Protocol detecter and parser disabled for SIP/UDP."); } + + + // register TCP parser + parser.ipproto = core::IPPROTO_TCP; + parser.probe_ts = Some(rs_sip_probing_parser_tcp_ts); + parser.probe_tc = Some(rs_sip_probing_parser_tcp_tc); + parser.parse_ts = rs_sip_parse_request_tcp; + parser.parse_tc = rs_sip_parse_response_tcp; + + let ip_proto_str = CString::new("tcp").unwrap(); + if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let alproto = AppLayerRegisterProtocolDetection(&parser, 1); + ALPROTO_SIP = alproto; + if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let _ = AppLayerRegisterParser(&parser, alproto); + } + } else { + SCLogDebug!("Protocol detector and parser disabled for SIP/TCP."); + } } From f7acf734c760f75405d25e7761ea155010ebd4c7 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 1 Aug 2023 21:20:58 +0200 Subject: [PATCH 3/6] rust/sip: add direction to transaction This patch permits to set a direction when a new transaction is created in order to avoid 'signature shadowing' as reported by Eric Leblond in commit 5aaf50760f546e9047da508f725f43a7ad9b8a35 --- rust/src/sip/sip.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/src/sip/sip.rs b/rust/src/sip/sip.rs index 97fa67057aea..6687b7819550 100755 --- a/rust/src/sip/sip.rs +++ b/rust/src/sip/sip.rs @@ -86,9 +86,9 @@ impl SIPState { self.transactions.clear(); } - fn new_tx(&mut self, _direction: crate::core::Direction) -> SIPTransaction { + fn new_tx(&mut self, direction: crate::core::Direction) -> SIPTransaction { self.tx_id += 1; - SIPTransaction::new(self.tx_id) + SIPTransaction::new(self.tx_id, direction) } fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SIPTransaction> { @@ -270,14 +270,14 @@ impl SIPState { } impl SIPTransaction { - pub fn new(id: u64) -> SIPTransaction { + pub fn new(id: u64, direction: crate::core::Direction) -> SIPTransaction { SIPTransaction { id, request: None, response: None, request_line: None, response_line: None, - tx_data: applayer::AppLayerTxData::new(), + tx_data: applayer::AppLayerTxData::for_direction(direction), } } } From ae800db9d726b99c865621a752076784da807dc9 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 1 Aug 2023 21:22:18 +0200 Subject: [PATCH 4/6] eve/schema: add sip_tcp --- etc/schema.json | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/etc/schema.json b/etc/schema.json index 1e945a276820..7472a944f21b 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -3772,7 +3772,10 @@ "rfb": { "$ref": "#/$defs/stats_applayer_error" }, - "sip": { + "sip_udp": { + "$ref": "#/$defs/stats_applayer_error" + }, + "sip_tcp": { "$ref": "#/$defs/stats_applayer_error" }, "smb": { @@ -3889,7 +3892,10 @@ "rfb": { "type": "integer" }, - "sip": { + "sip_udp": { + "type": "integer" + }, + "sip_tcp": { "type": "integer" }, "smb": { @@ -4000,7 +4006,10 @@ "rfb": { "type": "integer" }, - "sip": { + "sip_udp": { + "type": "integer" + }, + "sip_tcp": { "type": "integer" }, "smb": { From b624d2eaa15b33cdc0a0f467f87251de7aafbcb5 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 1 Aug 2023 21:23:19 +0200 Subject: [PATCH 5/6] output-json/sip: register logger for tcp This permits to log sip messages over tcp. --- src/output-json-sip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/output-json-sip.c b/src/output-json-sip.c index 8297be1cc3eb..394f53cc4124 100644 --- a/src/output-json-sip.c +++ b/src/output-json-sip.c @@ -88,6 +88,7 @@ static OutputInitResult OutputSIPLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) { AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_SIP); + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SIP); return OutputJsonLogInitSub(conf, parent_ctx); } From af2fef80fbdfff2525bb0a617d30121d1f14ed9f Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 1 Aug 2023 21:24:28 +0200 Subject: [PATCH 6/6] suricata.yaml: define SIP_PORTS --- suricata.yaml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/suricata.yaml.in b/suricata.yaml.in index 630399126dbe..7318a574429f 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -50,7 +50,7 @@ vars: GENEVE_PORTS: 6081 VXLAN_PORTS: 4789 TEREDO_PORTS: 3544 - + SIP_PORTS: "[5060, 5061]" ## ## Step 2: Select outputs to enable ##