From da35c0ed83bc270de05b1bd31c5a8a8748f4d89f Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Mon, 14 Aug 2023 20:10:36 +0200 Subject: [PATCH 1/8] rust/sip: rustfmt sip module --- rust/src/sip/detect.rs | 29 ++++------------ rust/src/sip/log.rs | 2 +- rust/src/sip/sip.rs | 79 +++++++++++++++++++++++------------------- 3 files changed, 51 insertions(+), 59 deletions(-) diff --git a/rust/src/sip/detect.rs b/rust/src/sip/detect.rs index 63f636529a34..91df4fb29932 100644 --- a/rust/src/sip/detect.rs +++ b/rust/src/sip/detect.rs @@ -23,9 +23,7 @@ use std::ptr; #[no_mangle] pub unsafe extern "C" fn rs_sip_tx_get_method( - tx: &mut SIPTransaction, - buffer: *mut *const u8, - buffer_len: *mut u32, + tx: &mut SIPTransaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Some(ref r) = tx.request { let m = &r.method; @@ -44,9 +42,7 @@ pub unsafe extern "C" fn rs_sip_tx_get_method( #[no_mangle] pub unsafe extern "C" fn rs_sip_tx_get_uri( - tx: &mut SIPTransaction, - buffer: *mut *const u8, - buffer_len: *mut u32, + tx: &mut SIPTransaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Some(ref r) = tx.request { let p = &r.path; @@ -65,10 +61,7 @@ pub unsafe extern "C" fn rs_sip_tx_get_uri( #[no_mangle] pub unsafe extern "C" fn rs_sip_tx_get_protocol( - tx: &mut SIPTransaction, - buffer: *mut *const u8, - buffer_len: *mut u32, - direction: u8, + tx: &mut SIPTransaction, buffer: *mut *const u8, buffer_len: *mut u32, direction: u8, ) -> u8 { match direction.into() { Direction::ToServer => { @@ -101,9 +94,7 @@ pub unsafe extern "C" fn rs_sip_tx_get_protocol( #[no_mangle] pub unsafe extern "C" fn rs_sip_tx_get_stat_code( - tx: &mut SIPTransaction, - buffer: *mut *const u8, - buffer_len: *mut u32, + tx: &mut SIPTransaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Some(ref r) = tx.response { let c = &r.code; @@ -122,9 +113,7 @@ pub unsafe extern "C" fn rs_sip_tx_get_stat_code( #[no_mangle] pub unsafe extern "C" fn rs_sip_tx_get_stat_msg( - tx: &mut SIPTransaction, - buffer: *mut *const u8, - buffer_len: *mut u32, + tx: &mut SIPTransaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Some(ref r) = tx.response { let re = &r.reason; @@ -143,9 +132,7 @@ pub unsafe extern "C" fn rs_sip_tx_get_stat_msg( #[no_mangle] pub unsafe extern "C" fn rs_sip_tx_get_request_line( - tx: &mut SIPTransaction, - buffer: *mut *const u8, - buffer_len: *mut u32, + tx: &mut SIPTransaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Some(ref r) = tx.request_line { if !r.is_empty() { @@ -163,9 +150,7 @@ pub unsafe extern "C" fn rs_sip_tx_get_request_line( #[no_mangle] pub unsafe extern "C" fn rs_sip_tx_get_response_line( - tx: &mut SIPTransaction, - buffer: *mut *const u8, - buffer_len: *mut u32, + tx: &mut SIPTransaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if let Some(ref r) = tx.response_line { if !r.is_empty() { diff --git a/rust/src/sip/log.rs b/rust/src/sip/log.rs index 792acfa49021..a7a98d5eb1b3 100644 --- a/rust/src/sip/log.rs +++ b/rust/src/sip/log.rs @@ -51,4 +51,4 @@ fn log(tx: &SIPTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> { #[no_mangle] pub extern "C" fn rs_sip_log_json(tx: &mut SIPTransaction, js: &mut JsonBuilder) -> bool { log(tx, js).is_ok() -} \ No newline at end of file +} diff --git a/rust/src/sip/sip.rs b/rust/src/sip/sip.rs index 4e86f5ea476d..d1aefde548be 100755 --- a/rust/src/sip/sip.rs +++ b/rust/src/sip/sip.rs @@ -17,10 +17,10 @@ // written by Giuseppe Longo -use crate::frames::*; use crate::applayer::{self, *}; use crate::core; use crate::core::{AppProto, Flow, ALPROTO_UNKNOWN}; +use crate::frames::*; use crate::sip::parser::*; use nom7::Err; use std; @@ -96,10 +96,7 @@ impl SIPState { } fn free_tx(&mut self, tx_id: u64) { - let tx = self - .transactions - .iter() - .position(|tx| tx.id == tx_id + 1); + let tx = self.transactions.iter().position(|tx| tx.id == tx_id + 1); debug_assert!(tx.is_some()); if let Some(idx) = tx { let _ = self.transactions.remove(idx); @@ -149,7 +146,13 @@ impl SIPState { 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); + let _pdu = Frame::new( + flow, + &stream_slice, + input, + input.len() as i64, + SIPFrameType::Pdu as u8, + ); SCLogDebug!("tc: pdu {:?}", _pdu); match sip_parse_response(input) { @@ -224,20 +227,40 @@ fn sip_frames_ts(flow: *const core::Flow, stream_slice: &StreamSlice, r: &Reques fn sip_frames_tc(flow: *const core::Flow, stream_slice: &StreamSlice, r: &Response) { let oi = stream_slice.as_slice(); - let _f = Frame::new(flow, stream_slice, oi, r.response_line_len as i64, SIPFrameType::ResponseLine as u8); - let hi = &oi[r.response_line_len as usize ..]; + let _f = Frame::new( + flow, + stream_slice, + oi, + r.response_line_len as i64, + SIPFrameType::ResponseLine as u8, + ); + let hi = &oi[r.response_line_len as usize..]; SCLogDebug!("tc: response_line {:?}", _f); - let _f = Frame::new(flow, stream_slice, hi, r.headers_len as i64, SIPFrameType::ResponseHeaders as u8); + let _f = Frame::new( + flow, + stream_slice, + hi, + r.headers_len as i64, + SIPFrameType::ResponseHeaders as u8, + ); SCLogDebug!("tc: response_headers {:?}", _f); if r.body_len > 0 { - let bi = &oi[r.body_offset as usize ..]; - let _f = Frame::new(flow, stream_slice, bi, r.body_len as i64, SIPFrameType::ResponseBody as u8); + let bi = &oi[r.body_offset as usize..]; + let _f = Frame::new( + flow, + stream_slice, + bi, + r.body_len as i64, + SIPFrameType::ResponseBody as u8, + ); SCLogDebug!("tc: response_body {:?}", _f); } } #[no_mangle] -pub extern "C" fn rs_sip_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void { +pub extern "C" fn rs_sip_state_new( + _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto, +) -> *mut std::os::raw::c_void { let state = SIPState::new(); let boxed = Box::new(state); return Box::into_raw(boxed) as *mut _; @@ -251,8 +274,7 @@ pub extern "C" fn rs_sip_state_free(state: *mut std::os::raw::c_void) { #[no_mangle] pub unsafe extern "C" fn rs_sip_state_get_tx( - state: *mut std::os::raw::c_void, - tx_id: u64, + state: *mut std::os::raw::c_void, tx_id: u64, ) -> *mut std::os::raw::c_void { let state = cast_pointer!(state, SIPState); match state.get_tx_by_id(tx_id) { @@ -275,8 +297,7 @@ pub unsafe extern "C" fn rs_sip_state_tx_free(state: *mut std::os::raw::c_void, #[no_mangle] pub extern "C" fn rs_sip_tx_get_alstate_progress( - _tx: *mut std::os::raw::c_void, - _direction: u8, + _tx: *mut std::os::raw::c_void, _direction: u8, ) -> std::os::raw::c_int { 1 } @@ -285,11 +306,7 @@ static mut ALPROTO_SIP: AppProto = ALPROTO_UNKNOWN; #[no_mangle] pub unsafe extern "C" fn rs_sip_probing_parser_ts( - _flow: *const Flow, - _direction: u8, - input: *const u8, - input_len: u32, - _rdir: *mut u8, + _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, ) -> AppProto { let buf = build_slice!(input, input_len as usize); if sip_parse_request(buf).is_ok() { @@ -300,11 +317,7 @@ pub unsafe extern "C" fn rs_sip_probing_parser_ts( #[no_mangle] pub unsafe extern "C" fn rs_sip_probing_parser_tc( - _flow: *const Flow, - _direction: u8, - input: *const u8, - input_len: u32, - _rdir: *mut u8, + _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, ) -> AppProto { let buf = build_slice!(input, input_len as usize); if sip_parse_response(buf).is_ok() { @@ -315,11 +328,8 @@ pub unsafe extern "C" fn rs_sip_probing_parser_tc( #[no_mangle] pub unsafe extern "C" fn rs_sip_parse_request( - 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, + 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_request(flow, stream_slice).into() @@ -327,11 +337,8 @@ pub unsafe extern "C" fn rs_sip_parse_request( #[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, - stream_slice: StreamSlice, - _data: *const std::os::raw::c_void, + 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(flow, stream_slice).into() From e960859d559f17a88cb0ef94b33ec82c5069e477 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 1 Aug 2023 20:50:17 +0200 Subject: [PATCH 2/8] sip/parser: accept valid chars Accepts valid characters as defined in RFC3261. --- rust/src/sip/parser.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/rust/src/sip/parser.rs b/rust/src/sip/parser.rs index a34bc2615e53..c7d616a498b4 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}; @@ -57,9 +57,13 @@ pub struct Response { pub body_len: u16, } +/** + * Valid tokens and chars are defined in RFC3261: + * https://www.rfc-editor.org/rfc/rfc3261#section-25.1 + */ #[inline] fn is_token_char(b: u8) -> bool { - is_alphanumeric(b) || b"!%'*+-._`".contains(&b) + is_alphanumeric(b) || b"!%'*+-._`~".contains(&b) } #[inline] @@ -69,7 +73,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 7d8988398bf10b668a192cfd74bc2ad4d3aacbb1 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Sat, 25 Nov 2023 09:39:54 +0100 Subject: [PATCH 3/8] sip/parser: enforce valid chars for sip version The `is_version_char` function incorrectly allowed characters that are not part of the valid SIP version "SIP/2.0". For instance, 'HTTP/1.1' was mistakenly accepted as a valid SIP version, although it's not. This commit fixes the issue by updating the condition to strictly check for the correct version string. --- rust/src/sip/parser.rs | 45 +++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/rust/src/sip/parser.rs b/rust/src/sip/parser.rs index c7d616a498b4..c64c652f5170 100644 --- a/rust/src/sip/parser.rs +++ b/rust/src/sip/parser.rs @@ -17,9 +17,9 @@ // written by Giuseppe Longo -use nom7::bytes::streaming::{take, take_while, take_while1}; +use nom7::bytes::streaming::{take, take_while, take_while1, tag}; use nom7::character::streaming::{char, crlf}; -use nom7::character::{is_alphabetic, is_alphanumeric, is_space}; +use nom7::character::{is_alphabetic, is_alphanumeric, is_space, is_digit}; use nom7::combinator::map_res; use nom7::sequence::delimited; use nom7::{Err, IResult, Needed}; @@ -78,7 +78,7 @@ fn is_request_uri_char(b: u8) -> bool { #[inline] fn is_version_char(b: u8) -> bool { - is_alphanumeric(b) || b"./".contains(&b) + is_digit(b) || b".".contains(&b) } #[inline] @@ -111,7 +111,7 @@ pub fn sip_parse_request(oi: &[u8]) -> IResult<&[u8], Request> { Request { method: method.into(), path: path.into(), - version: version.into(), + version, headers, request_line_len: request_line_len as u16, @@ -137,7 +137,7 @@ pub fn sip_parse_response(oi: &[u8]) -> IResult<&[u8], Response> { Ok(( bi, Response { - version: version.into(), + version, code: code.into(), reason: reason.into(), @@ -160,8 +160,10 @@ fn parse_request_uri(i: &[u8]) -> IResult<&[u8], &str> { } #[inline] -fn parse_version(i: &[u8]) -> IResult<&[u8], &str> { - map_res(take_while1(is_version_char), std::str::from_utf8)(i) +fn parse_version(i: &[u8]) -> IResult<&[u8], String> { + let (i, prefix) = map_res(tag("SIP/"), std::str::from_utf8)(i)?; + let (i, version) = map_res(take_while1(is_version_char), std::str::from_utf8)(i)?; + Ok((i, format!("{}{}", prefix, version))) } #[inline] @@ -326,4 +328,33 @@ mod tests { } } } + + #[test] + fn test_parse_invalid_version() { + let buf: &[u8] = "HTTP/1.1\r\n".as_bytes(); + + // This test must fail if 'HTTP/1.1' is accepted + match parse_version(buf) { + Ok((_, _)) => { + assert!(false); + } + Err(_) => { + assert!(true); + } + } + } + + #[test] + fn test_parse_valid_version() { + let buf: &[u8] = "SIP/2.0\r\n".as_bytes(); + + match parse_version(buf) { + Ok((_, version)) => { + assert_eq!(version, "SIP/2.0"); + } + Err(_) => { + assert!(false); + } + } + } } From 73665068e233452a4f669118182bc8f5535958be Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 1 Aug 2023 21:08:50 +0200 Subject: [PATCH 4/8] rust/sip: register parser for tcp This patch lets the parser to work over tcp protocol, taking care of handling data before calling the request/response parsers. Ticket #3351. --- etc/schema.json | 15 ++- rust/src/sip/sip.rs | 240 +++++++++++++++++++++++++++++++++++++++--- src/output-json-sip.c | 1 + 3 files changed, 238 insertions(+), 18 deletions(-) diff --git a/etc/schema.json b/etc/schema.json index 76d88ec4c8fb..43509530ce29 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -3810,7 +3810,10 @@ "rfb": { "$ref": "#/$defs/stats_applayer_error" }, - "sip": { + "sip_udp": { + "$ref": "#/$defs/stats_applayer_error" + }, + "sip_tcp": { "$ref": "#/$defs/stats_applayer_error" }, "smb": { @@ -3927,7 +3930,10 @@ "rfb": { "type": "integer" }, - "sip": { + "sip_udp": { + "type": "integer" + }, + "sip_tcp": { "type": "integer" }, "smb": { @@ -4038,7 +4044,10 @@ "rfb": { "type": "integer" }, - "sip": { + "sip_udp": { + "type": "integer" + }, + "sip_tcp": { "type": "integer" }, "smb": { diff --git a/rust/src/sip/sip.rs b/rust/src/sip/sip.rs index d1aefde548be..e203ae9e2eb7 100755 --- a/rust/src/sip/sip.rs +++ b/rust/src/sip/sip.rs @@ -19,7 +19,7 @@ use crate::applayer::{self, *}; use crate::core; -use crate::core::{AppProto, Flow, ALPROTO_UNKNOWN}; +use crate::core::{AppProto, Flow, ALPROTO_FAILED, ALPROTO_UNKNOWN}; use crate::frames::*; use crate::sip::parser::*; use nom7::Err; @@ -50,6 +50,8 @@ pub struct SIPState { state_data: AppLayerStateData, transactions: Vec, tx_id: u64, + request_frame: Option, + response_frame: Option, } impl State for SIPState { @@ -109,6 +111,15 @@ impl SIPState { } } + fn build_tx_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(); @@ -124,12 +135,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.build_tx_request(input, request); return true; } // app-layer-frame-documentation tag end: parse_request @@ -144,6 +150,68 @@ 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 mut start = input; + while !start.is_empty() { + if self.request_frame.is_none() { + self.request_frame = Frame::new( + flow, + &stream_slice, + start, + -1_i64, + SIPFrameType::Pdu as u8, + ); + SCLogDebug!("ts: pdu {:?}", self.request_frame); + } + match sip_parse_request(start) { + Ok((rem, request)) => { + sip_frames_ts(flow, &stream_slice, &request); + self.build_tx_request(start, request); + let consumed = start.len() - rem.len(); + start = rem; + + if let Some(frame) = &self.request_frame { + frame.set_len(flow, consumed as i64); + self.request_frame = None; + } + } + 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 build_tx_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( @@ -158,12 +226,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.build_tx_response(input, response); return true; } Err(Err::Incomplete(_)) => { @@ -176,6 +239,59 @@ 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 mut start = input; + while !start.is_empty() { + if self.response_frame.is_none() { + self.response_frame = Frame::new( + flow, + &stream_slice, + start, + -1_i64, + SIPFrameType::Pdu as u8, + ); + SCLogDebug!("tc: pdu {:?}", self.request_frame); + } + match sip_parse_response(start) { + Ok((rem, response)) => { + sip_frames_tc(flow, &stream_slice, &response); + self.build_tx_response(start, response); + let consumed = start.len() - rem.len(); + start = rem; + + if let Some(frame) = &self.response_frame { + frame.set_len(flow, consumed as i64); + self.response_frame = None; + } + } + 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 { @@ -315,6 +431,27 @@ 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.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, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, @@ -326,6 +463,27 @@ 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.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, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, @@ -335,6 +493,23 @@ 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, @@ -344,6 +519,23 @@ pub unsafe extern "C" fn rs_sip_parse_response( 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 { + 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_tcp(flow, stream_slice) +} + export_tx_data_get!(rs_sip_get_tx_data, SIPTransaction); export_state_data_get!(rs_sip_get_state_data, SIPState); @@ -352,7 +544,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, @@ -393,6 +585,24 @@ pub unsafe extern "C" fn rs_sip_register_parser() { let _ = AppLayerRegisterParser(&parser, alproto); } } else { - SCLogDebug!("Protocol detecter and parser disabled for SIP/UDP."); + SCLogDebug!("Protocol detection and parsing disabled for UDP SIP."); + } + + // 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 detection and parsing disabled for TCP SIP."); } } diff --git a/src/output-json-sip.c b/src/output-json-sip.c index 7dd442cf6aba..f147a755e28d 100644 --- a/src/output-json-sip.c +++ b/src/output-json-sip.c @@ -77,6 +77,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 a3f8409211465dc0fb2ad6006316f270b4f3e74f Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 1 Aug 2023 21:20:58 +0200 Subject: [PATCH 5/8] 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 e203ae9e2eb7..902844f30efa 100755 --- a/rust/src/sip/sip.rs +++ b/rust/src/sip/sip.rs @@ -88,9 +88,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> { @@ -295,14 +295,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 bdbdf8f9af3f1d5cb8abe6d3cdecda43c9ebff41 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 1 Aug 2023 21:24:28 +0200 Subject: [PATCH 6/8] suricata.yaml: define SIP_PORTS --- doc/userguide/configuration/suricata-yaml.rst | 1 + suricata.yaml.in | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/userguide/configuration/suricata-yaml.rst b/doc/userguide/configuration/suricata-yaml.rst index 0b39705d896b..8724a9b3ae5c 100644 --- a/doc/userguide/configuration/suricata-yaml.rst +++ b/doc/userguide/configuration/suricata-yaml.rst @@ -2442,6 +2442,7 @@ address, you should enter 'any'. SHELLCODE_PORTS: "!80" ORACLE_PORTS: 1521 SSH_PORTS: 22 + SIP_PORTS: "[5060, 5061]" .. _host-os-policy: diff --git a/suricata.yaml.in b/suricata.yaml.in index 630399126dbe..7b337e11bff9 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -50,6 +50,7 @@ vars: GENEVE_PORTS: 6081 VXLAN_PORTS: 4789 TEREDO_PORTS: 3544 + SIP_PORTS: "[5060, 5061]" ## ## Step 2: Select outputs to enable From 2ececf054653e813b7205cef6ba053a15c0d0fb5 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Fri, 24 Nov 2023 23:18:49 +0100 Subject: [PATCH 7/8] rust/sip: register pattern matching This permits to detect the SIP protocol using pattern matching instead of probing parser. Since it is no longer used, the respective probing functions have been removed. --- rust/src/sip/sip.rs | 129 ++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 71 deletions(-) diff --git a/rust/src/sip/sip.rs b/rust/src/sip/sip.rs index 902844f30efa..0175a2b1a627 100755 --- a/rust/src/sip/sip.rs +++ b/rust/src/sip/sip.rs @@ -19,7 +19,7 @@ use crate::applayer::{self, *}; use crate::core; -use crate::core::{AppProto, Flow, ALPROTO_FAILED, ALPROTO_UNKNOWN}; +use crate::core::{AppProto, ALPROTO_UNKNOWN}; use crate::frames::*; use crate::sip::parser::*; use nom7::Err; @@ -420,70 +420,6 @@ pub extern "C" fn rs_sip_tx_get_alstate_progress( static mut ALPROTO_SIP: AppProto = ALPROTO_UNKNOWN; -#[no_mangle] -pub unsafe extern "C" fn rs_sip_probing_parser_ts( - _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, -) -> AppProto { - let buf = build_slice!(input, input_len as usize); - if sip_parse_request(buf).is_ok() { - return ALPROTO_SIP; - } - 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.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, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, -) -> AppProto { - let buf = build_slice!(input, input_len as usize); - if sip_parse_response(buf).is_ok() { - return ALPROTO_SIP; - } - 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.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, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, @@ -536,6 +472,52 @@ pub unsafe extern "C" fn rs_sip_parse_response_tcp( state.parse_response_tcp(flow, stream_slice) } +fn register_pattern_probe(proto: u8) -> i8 { + let methods: Vec<&str> = vec![ + "REGISTER\0", + "INVITE\0", + "ACK\0", + "BYE\0", + "CANCEL\0", + "UPDATE\0", + "REFER\0", + "PRACK\0", + "SUBSCRIBE\0", + "NOTIFY\0", + "PUBLISH\0", + "MESSAGE\0", + "INFO\0", + ]; + let mut r = 0; + unsafe { + for method in methods { + let depth = (method.len() - 1) as u16; + r |= AppLayerProtoDetectPMRegisterPatternCS( + proto, + ALPROTO_SIP, + method.as_ptr() as *const std::os::raw::c_char, + depth, + 0, + core::Direction::ToServer as u8, + ); + } + r |= AppLayerProtoDetectPMRegisterPatternCS( + proto, + ALPROTO_SIP, + b"SIP/2.0\0".as_ptr() as *const std::os::raw::c_char, + 8, + 0, + core::Direction::ToClient as u8, + ); + } + + if r == 0 { + return 0; + } else { + return -1; + } +} + export_tx_data_get!(rs_sip_get_tx_data, SIPTransaction); export_state_data_get!(rs_sip_get_state_data, SIPState); @@ -543,13 +525,12 @@ 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 mut parser = RustParser { name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, - default_port: default_port.as_ptr(), + default_port: std::ptr::null(), ipproto: core::IPPROTO_UDP, - probe_ts: Some(rs_sip_probing_parser_ts), - probe_tc: Some(rs_sip_probing_parser_tc), + probe_ts: None, + probe_tc: None, min_depth: 0, max_depth: 16, state_new: rs_sip_state_new, @@ -584,14 +565,17 @@ pub unsafe extern "C" fn rs_sip_register_parser() { if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { let _ = AppLayerRegisterParser(&parser, alproto); } + if register_pattern_probe(core::IPPROTO_UDP) < 0 { + return; + } } else { SCLogDebug!("Protocol detection and parsing disabled for UDP SIP."); } // 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.probe_ts = None; + parser.probe_tc = None; parser.parse_ts = rs_sip_parse_request_tcp; parser.parse_tc = rs_sip_parse_response_tcp; @@ -602,6 +586,9 @@ pub unsafe extern "C" fn rs_sip_register_parser() { if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { let _ = AppLayerRegisterParser(&parser, alproto); } + if register_pattern_probe(core::IPPROTO_TCP) < 0 { + return; + } } else { SCLogDebug!("Protocol detection and parsing disabled for TCP SIP."); } From 4d6c05fc63e7fc57d1b177226ce5ad30e5a4d472 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Fri, 24 Nov 2023 09:54:53 +0100 Subject: [PATCH 8/8] doc: add upgrade section for 8 --- doc/userguide/upgrade.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index fd6e27501047..345087fe55b3 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -38,6 +38,19 @@ Upgrading 7.0 to 8.0 -------------------- .. note:: ``stats.whitelist`` has been renamed to ``stats.score`` in ``eve.json`` +Major changes +~~~~~~~~~~~~~ +- SIP parser has been updated to inspect traffic carried by TCP as well. + SIP keywords can still match on their respective fields in addition + to these improvements. + Transactions are logged with the same schema regardless of which + transport protocol is carrying the payload. + Also, SIP protocol is detected using pattern matching and not only + probing parser. +- ``SIP_PORTS`` variable has been introduced in suricata.yaml +- Application layer's ``sip`` counter has been split into ``sip_tcp`` and ``sip_udp`` + for the ``stats`` event. + Upgrading 6.0 to 7.0 --------------------