From: Philippe Antoine Date: Thu, 5 Mar 2020 14:11:00 +0000 (+0100) Subject: parse: move SSH parser from C to Rust X-Git-Tag: suricata-6.0.0-beta1~414 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=69b4fffdae19e033b18b36f627cc666b5daec625;p=thirdparty%2Fsuricata.git parse: move SSH parser from C to Rust --- diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a510cc736d..68ee041a00 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -71,3 +71,4 @@ pub mod rfb; pub mod applayertemplate; pub mod rdp; pub mod x509; +pub mod ssh; diff --git a/rust/src/ssh/detect.rs b/rust/src/ssh/detect.rs new file mode 100644 index 0000000000..8b2b0dfe08 --- /dev/null +++ b/rust/src/ssh/detect.rs @@ -0,0 +1,92 @@ +/* Copyright (C) 2020 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * 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 + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use super::ssh::SSHTransaction; +use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER}; +use std::ptr; + +#[no_mangle] +pub extern "C" fn rs_ssh_tx_get_protocol( + tx: *mut std::os::raw::c_void, buffer: *mut *const u8, buffer_len: *mut u32, direction: u8, +) -> u8 { + let tx = cast_pointer!(tx, SSHTransaction); + match direction { + STREAM_TOSERVER => { + let m = &tx.cli_hdr.protover; + if m.len() > 0 { + unsafe { + *buffer = m.as_ptr(); + *buffer_len = m.len() as u32; + } + return 1; + } + } + STREAM_TOCLIENT => { + let m = &tx.srv_hdr.protover; + if m.len() > 0 { + unsafe { + *buffer = m.as_ptr(); + *buffer_len = m.len() as u32; + } + return 1; + } + } + _ => {} + } + unsafe { + *buffer = ptr::null(); + *buffer_len = 0; + } + + return 0; +} + +#[no_mangle] +pub extern "C" fn rs_ssh_tx_get_software( + tx: *mut std::os::raw::c_void, buffer: *mut *const u8, buffer_len: *mut u32, direction: u8, +) -> u8 { + let tx = cast_pointer!(tx, SSHTransaction); + match direction { + STREAM_TOSERVER => { + let m = &tx.cli_hdr.swver; + if m.len() > 0 { + unsafe { + *buffer = m.as_ptr(); + *buffer_len = m.len() as u32; + } + return 1; + } + } + STREAM_TOCLIENT => { + let m = &tx.srv_hdr.swver; + if m.len() > 0 { + unsafe { + *buffer = m.as_ptr(); + *buffer_len = m.len() as u32; + } + return 1; + } + } + _ => {} + } + unsafe { + *buffer = ptr::null(); + *buffer_len = 0; + } + + return 0; +} diff --git a/rust/src/ssh/logger.rs b/rust/src/ssh/logger.rs new file mode 100644 index 0000000000..000d7afb7c --- /dev/null +++ b/rust/src/ssh/logger.rs @@ -0,0 +1,64 @@ +/* Copyright (C) 2020 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * 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 + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use super::ssh::SSHTransaction; +use crate::json::*; + +fn log_ssh(tx: &SSHTransaction) -> Option { + if tx.cli_hdr.protover.len() == 0 && tx.srv_hdr.protover.len() == 0 { + return None; + } + let js = Json::object(); + if tx.cli_hdr.protover.len() > 0 { + let cjs = Json::object(); + cjs.set_string_from_bytes( + "proto_version", + &tx.cli_hdr.protover, + ); + if tx.cli_hdr.swver.len() > 0 { + cjs.set_string_from_bytes( + "software_version", + &tx.cli_hdr.swver, + ); + } + js.set("client", cjs); + } + if tx.srv_hdr.protover.len() > 0 { + let sjs = Json::object(); + sjs.set_string_from_bytes( + "proto_version", + &tx.srv_hdr.protover, + ); + if tx.srv_hdr.swver.len() > 0 { + sjs.set_string_from_bytes( + "software_version", + &tx.srv_hdr.swver, + ); + } + js.set("server", sjs); + } + return Some(js); +} + +#[no_mangle] +pub extern "C" fn rs_ssh_log_json(tx: *mut std::os::raw::c_void) -> *mut JsonT { + let tx = cast_pointer!(tx, SSHTransaction); + match log_ssh(tx) { + Some(js) => js.unwrap(), + None => std::ptr::null_mut(), + } +} diff --git a/rust/src/ssh/mod.rs b/rust/src/ssh/mod.rs new file mode 100644 index 0000000000..12efccd0a5 --- /dev/null +++ b/rust/src/ssh/mod.rs @@ -0,0 +1,21 @@ +/* Copyright (C) 2020 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * 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 + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +pub mod detect; +pub mod logger; +mod parser; +pub mod ssh; diff --git a/rust/src/ssh/parser.rs b/rust/src/ssh/parser.rs new file mode 100644 index 0000000000..1d2ad8439c --- /dev/null +++ b/rust/src/ssh/parser.rs @@ -0,0 +1,196 @@ +/* Copyright (C) 2020 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * 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 + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use nom::combinator::rest; +use nom::number::streaming::{be_u32, be_u8}; +use std::fmt; + +#[inline] +fn is_not_lineend(b: u8) -> bool { + if b == 10 || b == 13 { + return false; + } + return true; +} + +//may leave \r at the end to be removed +named!(pub ssh_parse_line<&[u8], &[u8]>, + terminated!( + take_while!(is_not_lineend), + alt!( tag!("\n") | tag!("\r\n") | + do_parse!( + bytes: tag!("\r") >> + not!(eof!()) >> (bytes) + ) + ) + ) +); + +#[derive(PartialEq)] +pub struct SshBanner<'a> { + pub protover: &'a [u8], + pub swver: &'a [u8], +} + +// Could be simplified adding dummy \n at the end +// or use nom5 nom::bytes::complete::is_not +named!(pub ssh_parse_banner, + do_parse!( + tag!("SSH-") >> + protover: is_not!("-") >> + char!('-') >> + swver: alt!( complete!( is_not!(" \r\n") ) | rest ) >> + //remaining after space is comments + (SshBanner{protover, swver}) + ) +); + +#[derive(PartialEq)] +pub struct SshRecordHeader { + pub pkt_len: u32, + padding_len: u8, + pub msg_code: u8, +} + +impl fmt::Display for SshRecordHeader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "(pkt_len:{}, padding_len:{}, msg_code:{})", + self.pkt_len, self.padding_len, self.msg_code + ) + } +} + +named!(pub ssh_parse_record_header, + do_parse!( + pkt_len: verify!(be_u32, |&val| val > 1) >> + padding_len: be_u8 >> + msg_code: be_u8 >> + (SshRecordHeader{pkt_len, padding_len, msg_code}) + ) +); + +//test for evasion against pkt_len=0or1... +named!(pub ssh_parse_record, + do_parse!( + pkt_len: verify!(be_u32, |&val| val > 1) >> + padding_len: be_u8 >> + msg_code: be_u8 >> + take!((pkt_len-2) as usize) >> + (SshRecordHeader{pkt_len, padding_len, msg_code}) + ) +); + +#[cfg(test)] +mod tests { + + use super::*; + + /// Simple test of some valid data. + #[test] + fn test_ssh_parse_banner() { + let buf = b"SSH-Single-"; + let result = ssh_parse_banner(buf); + match result { + Ok((_, message)) => { + // Check the first message. + assert_eq!(message.protover, b"Single"); + assert_eq!(message.swver, b""); + } + Err(err) => { + panic!("Result should not be an error: {:?}.", err); + } + } + let buf2 = b"SSH-2.0-Soft"; + let result2 = ssh_parse_banner(buf2); + match result2 { + Ok((_, message)) => { + // Check the first message. + assert_eq!(message.protover, b"2.0"); + assert_eq!(message.swver, b"Soft"); + } + Err(err) => { + panic!("Result should not be an error: {:?}.", err); + } + } + } + + #[test] + fn test_parse_line() { + let buf = b"SSH-Single\n"; + let result = ssh_parse_line(buf); + match result { + Ok((_, message)) => { + // Check the first message. + assert_eq!(message, b"SSH-Single"); + } + Err(err) => { + panic!("Result should not be an error: {:?}.", err); + } + } + let buf2 = b"SSH-Double\r\n"; + let result2 = ssh_parse_line(buf2); + match result2 { + Ok((_, message)) => { + // Check the first message. + assert_eq!(message, b"SSH-Double"); + } + Err(err) => { + panic!("Result should not be an error: {:?}.", err); + } + } + let buf3 = b"SSH-Oops\rMore\r\n"; + let result3 = ssh_parse_line(buf3); + match result3 { + Ok((rem, message)) => { + // Check the first message. + assert_eq!(message, b"SSH-Oops"); + assert_eq!(rem, b"More\r\n"); + } + Err(err) => { + panic!("Result should not be an error: {:?}.", err); + } + } + let buf4 = b"SSH-Miss\r"; + let result4 = ssh_parse_line(buf4); + match result4 { + Ok((_, _)) => { + panic!("Expected incomplete result"); + } + Err(nom::Err::Incomplete(_)) => { + //OK + assert_eq!(1, 1); + } + Err(err) => { + panic!("Result should not be an error: {:?}.", err); + } + } + let buf5 = b"\n"; + let result5 = ssh_parse_line(buf5); + match result5 { + Ok((_, message)) => { + // Check empty line + assert_eq!(message, b""); + } + Err(err) => { + panic!("Result should not be an error: {:?}.", err); + } + } + } + +} diff --git a/rust/src/ssh/ssh.rs b/rust/src/ssh/ssh.rs new file mode 100644 index 0000000000..eba07c63c6 --- /dev/null +++ b/rust/src/ssh/ssh.rs @@ -0,0 +1,531 @@ +/* Copyright (C) 2020 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * 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 + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use super::parser; +use crate::applayer::*; +use crate::core::STREAM_TOSERVER; +use crate::core::{self, AppProto, Flow, ALPROTO_UNKNOWN, IPPROTO_TCP}; +use crate::log::*; +use std::ffi::{CStr, CString}; +use std::mem::transmute; + +static mut ALPROTO_SSH: AppProto = ALPROTO_UNKNOWN; + +#[repr(u32)] +pub enum SSHEvent { + InvalidBanner = 0, + LongBanner, + InvalidRecord, +} + +impl SSHEvent { + fn from_i32(value: i32) -> Option { + match value { + 0 => Some(SSHEvent::InvalidBanner), + 1 => Some(SSHEvent::LongBanner), + 2 => Some(SSHEvent::InvalidRecord), + _ => None, + } + } +} + +#[repr(u8)] +#[derive(Copy, Clone, PartialOrd, PartialEq)] +pub enum SSHConnectionState { + SshStateInProgress = 0, + SshStateBannerWaitEol = 1, + SshStateBannerDone = 2, + SshStateFinished = 3, +} + +const SSH_MAX_BANNER_LEN: usize = 256; +const SSH_RECORD_HEADER_LEN: usize = 6; +//TODO complete enum and parse messages contents +const SSH_MSG_NEWKEYS: u8 = 21; + +pub struct SshHeader { + record_left: u32, + flags: SSHConnectionState, + pub protover: Vec, + pub swver: Vec, +} + +impl SshHeader { + pub fn new() -> SshHeader { + SshHeader { + record_left: 0, + flags: SSHConnectionState::SshStateInProgress, + protover: Vec::new(), + swver: Vec::new(), + } + } +} + +pub struct SSHTransaction { + pub srv_hdr: SshHeader, + pub cli_hdr: SshHeader, + + logged: LoggerFlags, + de_state: Option<*mut core::DetectEngineState>, + detect_flags: TxDetectFlags, + events: *mut core::AppLayerDecoderEvents, +} + +impl SSHTransaction { + pub fn new() -> SSHTransaction { + SSHTransaction { + srv_hdr: SshHeader::new(), + cli_hdr: SshHeader::new(), + logged: LoggerFlags::new(), + de_state: None, + detect_flags: TxDetectFlags::default(), + events: std::ptr::null_mut(), + } + } + + pub fn free(&mut self) { + if self.events != std::ptr::null_mut() { + core::sc_app_layer_decoder_events_free_events(&mut self.events); + } + if let Some(state) = self.de_state { + core::sc_detect_engine_state_free(state); + } + } +} + +impl Drop for SSHTransaction { + fn drop(&mut self) { + self.free(); + } +} + +pub struct SSHState { + transaction: SSHTransaction, +} + +impl SSHState { + pub fn new() -> Self { + Self { + transaction: SSHTransaction::new(), + } + } + + fn set_event(&mut self, event: SSHEvent) { + let ev = event as u8; + core::sc_app_layer_decoder_events_set_event_raw(&mut self.transaction.events, ev); + } + + fn parse_record( + &mut self, mut input: &[u8], resp: bool, pstate: *mut std::os::raw::c_void, + ) -> AppLayerResult { + let (mut hdr, ohdr) = if !resp { + (&mut self.transaction.cli_hdr, &self.transaction.srv_hdr) + } else { + (&mut self.transaction.srv_hdr, &self.transaction.cli_hdr) + }; + let il = input.len(); + //first skip record left bytes + if hdr.record_left > 0 { + //should we check for overflow ? + let ilen = input.len() as u32; + if hdr.record_left >= ilen { + hdr.record_left -= ilen; + return AppLayerResult::ok(); + } else { + let start = hdr.record_left as usize; + input = &input[start..]; + hdr.record_left = 0; + } + } + //parse records out of input + while input.len() > 0 { + match parser::ssh_parse_record(input) { + Ok((rem, head)) => { + SCLogDebug!("SSH valid record {}", head); + input = rem; + if head.msg_code == SSH_MSG_NEWKEYS { + hdr.flags = SSHConnectionState::SshStateFinished; + if ohdr.flags >= SSHConnectionState::SshStateFinished { + unsafe { + AppLayerParserStateSetFlag( + pstate, + APP_LAYER_PARSER_NO_INSPECTION + | APP_LAYER_PARSER_NO_REASSEMBLY + | APP_LAYER_PARSER_BYPASS_READY, + ); + } + } + } + //header and complete data (not returned) + } + Err(nom::Err::Incomplete(_)) => { + match parser::ssh_parse_record_header(input) { + Ok((rem, head)) => { + SCLogDebug!("SSH valid record header {}", head); + let remlen = rem.len() as u32; + hdr.record_left = head.pkt_len - 2 - remlen; + //header with rem as incomplete data + if head.msg_code == SSH_MSG_NEWKEYS { + hdr.flags = SSHConnectionState::SshStateFinished; + } + return AppLayerResult::ok(); + } + Err(nom::Err::Incomplete(_)) => { + //we may have consumed data from previous records + if input.len() < SSH_RECORD_HEADER_LEN { + //do not trust nom incomplete value + return AppLayerResult::incomplete( + (il - input.len()) as u32, + SSH_RECORD_HEADER_LEN as u32, + ); + } else { + panic!("SSH invalid length record header"); + } + } + Err(e) => { + SCLogDebug!("SSH invalid record header {}", e); + self.set_event(SSHEvent::InvalidRecord); + return AppLayerResult::err(); + } + } + } + Err(e) => { + SCLogDebug!("SSH invalid record {}", e); + self.set_event(SSHEvent::InvalidRecord); + return AppLayerResult::err(); + } + } + } + return AppLayerResult::ok(); + } + + fn parse_banner( + &mut self, input: &[u8], resp: bool, pstate: *mut std::os::raw::c_void, + ) -> AppLayerResult { + let mut hdr = if !resp { + &mut self.transaction.cli_hdr + } else { + &mut self.transaction.srv_hdr + }; + if hdr.flags == SSHConnectionState::SshStateBannerWaitEol { + match parser::ssh_parse_line(input) { + Ok((rem, _)) => { + return self.parse_record(rem, resp, pstate); + } + Err(nom::Err::Incomplete(_)) => { + return AppLayerResult::incomplete(0 as u32, (input.len() + 1) as u32); + } + Err(e) => { + SCLogDebug!("SSH invalid banner {}", e); + self.set_event(SSHEvent::InvalidBanner); + return AppLayerResult::err(); + } + } + } + match parser::ssh_parse_line(input) { + Ok((rem, line)) => { + if let Ok((_, banner)) = parser::ssh_parse_banner(line) { + hdr.protover.extend(banner.protover); + if banner.swver.len() > 0 { + hdr.swver.extend(banner.swver); + } + hdr.flags = SSHConnectionState::SshStateBannerDone; + } else { + SCLogDebug!("SSH invalid banner"); + self.set_event(SSHEvent::InvalidBanner); + return AppLayerResult::err(); + } + if line.len() >= SSH_MAX_BANNER_LEN { + SCLogDebug!( + "SSH banner too long {} vs {}", + line.len(), + SSH_MAX_BANNER_LEN + ); + self.set_event(SSHEvent::LongBanner); + } + return self.parse_record(rem, resp, pstate); + } + Err(nom::Err::Incomplete(_)) => { + if input.len() < SSH_MAX_BANNER_LEN { + //0 consumed, needs at least one more byte + return AppLayerResult::incomplete(0 as u32, (input.len() + 1) as u32); + } else { + SCLogDebug!( + "SSH banner too long {} vs {} and waiting for eol", + input.len(), + SSH_MAX_BANNER_LEN + ); + if let Ok((_, banner)) = parser::ssh_parse_banner(input) { + hdr.protover.extend(banner.protover); + if banner.swver.len() > 0 { + hdr.swver.extend(banner.swver); + } + hdr.flags = SSHConnectionState::SshStateBannerWaitEol; + self.set_event(SSHEvent::LongBanner); + return AppLayerResult::ok(); + } else { + self.set_event(SSHEvent::InvalidBanner); + return AppLayerResult::err(); + } + } + } + Err(e) => { + SCLogDebug!("SSH invalid banner {}", e); + self.set_event(SSHEvent::InvalidBanner); + return AppLayerResult::err(); + } + } + } +} + +// C exports. + +export_tx_get_detect_state!(rs_ssh_tx_get_detect_state, SSHTransaction); +export_tx_set_detect_state!(rs_ssh_tx_set_detect_state, SSHTransaction); + +export_tx_detect_flags_set!(rs_ssh_set_tx_detect_flags, SSHTransaction); +export_tx_detect_flags_get!(rs_ssh_get_tx_detect_flags, SSHTransaction); + +#[no_mangle] +pub extern "C" fn rs_ssh_state_get_events( + tx: *mut std::os::raw::c_void, +) -> *mut core::AppLayerDecoderEvents { + let tx = cast_pointer!(tx, SSHTransaction); + return tx.events; +} + +#[no_mangle] +pub extern "C" fn rs_ssh_state_get_event_info( + event_name: *const std::os::raw::c_char, event_id: *mut std::os::raw::c_int, + event_type: *mut core::AppLayerEventType, +) -> std::os::raw::c_int { + if event_name == std::ptr::null() { + return -1; + } + let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) }; + let event = match c_event_name.to_str() { + Ok(s) => { + match s { + "invalid_banner" => SSHEvent::InvalidBanner as i32, + "long_banner" => SSHEvent::LongBanner as i32, + "invalid_record" => SSHEvent::InvalidRecord as i32, + _ => -1, // unknown event + } + } + Err(_) => -1, // UTF-8 conversion failed + }; + unsafe { + *event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION; + *event_id = event as std::os::raw::c_int; + }; + 0 +} + +#[no_mangle] +pub extern "C" fn rs_ssh_state_get_event_info_by_id( + event_id: std::os::raw::c_int, event_name: *mut *const std::os::raw::c_char, + event_type: *mut core::AppLayerEventType, +) -> i8 { + if let Some(e) = SSHEvent::from_i32(event_id as i32) { + let estr = match e { + SSHEvent::InvalidBanner => "invalid_banner\0", + SSHEvent::LongBanner => "long_banner\0", + SSHEvent::InvalidRecord => "invalid_record\0", + }; + unsafe { + *event_name = estr.as_ptr() as *const std::os::raw::c_char; + *event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION; + }; + 0 + } else { + -1 + } +} + +#[no_mangle] +pub extern "C" fn rs_ssh_state_new() -> *mut std::os::raw::c_void { + let state = SSHState::new(); + let boxed = Box::new(state); + return unsafe { transmute(boxed) }; +} + +#[no_mangle] +pub extern "C" fn rs_ssh_state_free(state: *mut std::os::raw::c_void) { + // Just unbox... + let _drop: Box = unsafe { transmute(state) }; +} + +#[no_mangle] +pub extern "C" fn rs_ssh_state_tx_free(_state: *mut std::os::raw::c_void, _tx_id: u64) { + //do nothing +} + +#[no_mangle] +pub extern "C" fn rs_ssh_parse_request( + _flow: *const Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void, + input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8, +) -> AppLayerResult { + let state = &mut cast_pointer!(state, SSHState); + let buf = build_slice!(input, input_len as usize); + let hdr = &mut state.transaction.cli_hdr; + if hdr.flags < SSHConnectionState::SshStateBannerDone { + return state.parse_banner(buf, false, pstate); + } else { + return state.parse_record(buf, false, pstate); + } +} + +#[no_mangle] +pub extern "C" fn rs_ssh_parse_response( + _flow: *const Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void, + input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8, +) -> AppLayerResult { + let state = &mut cast_pointer!(state, SSHState); + let buf = build_slice!(input, input_len as usize); + let hdr = &mut state.transaction.srv_hdr; + if hdr.flags < SSHConnectionState::SshStateBannerDone { + return state.parse_banner(buf, true, pstate); + } else { + return state.parse_record(buf, true, pstate); + } +} + +#[no_mangle] +pub extern "C" fn rs_ssh_state_get_tx( + state: *mut std::os::raw::c_void, _tx_id: u64, +) -> *mut std::os::raw::c_void { + let state = cast_pointer!(state, SSHState); + return unsafe { transmute(&state.transaction) }; +} + +#[no_mangle] +pub extern "C" fn rs_ssh_state_get_tx_count(_state: *mut std::os::raw::c_void) -> u64 { + return 1; +} + +#[no_mangle] +pub extern "C" fn rs_ssh_state_progress_completion_status(_direction: u8) -> std::os::raw::c_int { + return SSHConnectionState::SshStateFinished as i32; +} + +#[no_mangle] +pub extern "C" fn rs_ssh_tx_get_flags( + tx: *mut std::os::raw::c_void, direction: u8, +) -> SSHConnectionState { + let tx = cast_pointer!(tx, SSHTransaction); + if direction == STREAM_TOSERVER { + return tx.cli_hdr.flags; + } else { + return tx.srv_hdr.flags; + } +} + +#[no_mangle] +pub extern "C" fn rs_ssh_tx_get_alstate_progress( + tx: *mut std::os::raw::c_void, direction: u8, +) -> std::os::raw::c_int { + let tx = cast_pointer!(tx, SSHTransaction); + + if tx.cli_hdr.flags >= SSHConnectionState::SshStateFinished + && tx.srv_hdr.flags >= SSHConnectionState::SshStateFinished + { + return SSHConnectionState::SshStateFinished as i32; + } + + if direction == STREAM_TOSERVER { + if tx.cli_hdr.flags >= SSHConnectionState::SshStateBannerDone { + return SSHConnectionState::SshStateBannerDone as i32; + } + } else { + if tx.srv_hdr.flags >= SSHConnectionState::SshStateBannerDone { + return SSHConnectionState::SshStateBannerDone as i32; + } + } + + return SSHConnectionState::SshStateInProgress as i32; +} + +#[no_mangle] +pub extern "C" fn rs_ssh_tx_get_logged( + _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void, +) -> u32 { + let tx = cast_pointer!(tx, SSHTransaction); + return tx.logged.get(); +} + +#[no_mangle] +pub extern "C" fn rs_ssh_tx_set_logged( + _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void, logged: u32, +) { + let tx = cast_pointer!(tx, SSHTransaction); + tx.logged.set(logged); +} + +// Parser name as a C style string. +const PARSER_NAME: &'static [u8] = b"ssh\0"; + +#[no_mangle] +pub unsafe extern "C" fn rs_ssh_register_parser() { + let default_port = CString::new("[22]").unwrap(); + let parser = RustParser { + name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, + default_port: default_port.as_ptr(), + ipproto: IPPROTO_TCP, + //simple patterns, no probing + probe_ts: None, + probe_tc: None, + min_depth: 0, + max_depth: 0, + state_new: rs_ssh_state_new, + state_free: rs_ssh_state_free, + tx_free: rs_ssh_state_tx_free, + parse_ts: rs_ssh_parse_request, + parse_tc: rs_ssh_parse_response, + get_tx_count: rs_ssh_state_get_tx_count, + get_tx: rs_ssh_state_get_tx, + tx_get_comp_st: rs_ssh_state_progress_completion_status, + tx_get_progress: rs_ssh_tx_get_alstate_progress, + get_tx_logged: Some(rs_ssh_tx_get_logged), + set_tx_logged: Some(rs_ssh_tx_set_logged), + get_de_state: rs_ssh_tx_get_detect_state, + set_de_state: rs_ssh_tx_set_detect_state, + get_events: Some(rs_ssh_state_get_events), + get_eventinfo: Some(rs_ssh_state_get_event_info), + get_eventinfo_byid: Some(rs_ssh_state_get_event_info_by_id), + localstorage_new: None, + localstorage_free: None, + get_tx_mpm_id: None, + set_tx_mpm_id: None, + get_files: None, + get_tx_iterator: None, + get_tx_detect_flags: Some(rs_ssh_get_tx_detect_flags), + set_tx_detect_flags: Some(rs_ssh_set_tx_detect_flags), + }; + + let ip_proto_str = CString::new("tcp").unwrap(); + + if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let alproto = AppLayerRegisterProtocolDetection(&parser, 1); + ALPROTO_SSH = alproto; + if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let _ = AppLayerRegisterParser(&parser, alproto); + } + SCLogDebug!("Rust ssh parser registered."); + } else { + SCLogNotice!("Protocol detector and parser disabled for SSH."); + } +} diff --git a/src/alert-prelude.c b/src/alert-prelude.c index 81f8bf1d9a..33c853c6bd 100644 --- a/src/alert-prelude.c +++ b/src/alert-prelude.c @@ -724,17 +724,17 @@ static void PacketToDataProtoTLS(const Packet *p, const PacketAlert *pa, idmef_a static void PacketToDataProtoSSH(const Packet *p, const PacketAlert *pa, idmef_alert_t *alert) { json_t *js, *s_js; - SshState *ssh_state = (SshState *)FlowGetAppState(p->flow); + void *ssh_state = FlowGetAppState(p->flow); if (ssh_state == NULL) return; - js = json_object(); - if (js == NULL) + void *tx_ptr = rs_ssh_state_get_tx(ssh_state, 0); + BUG_ON(tx_ptr == NULL); + js = rs_ssh_log_json(tx_ptr); + if (unlikely(js == NULL)) return; - JsonSshLogJSON(js, ssh_state); - s_js = json_object_get(js, "server"); if (s_js != NULL) { JsonToAdditionalData(NULL, s_js, alert); diff --git a/src/app-layer-ssh.c b/src/app-layer-ssh.c index 910803f4ab..44b4004870 100644 --- a/src/app-layer-ssh.c +++ b/src/app-layer-ssh.c @@ -41,6 +41,7 @@ #include "app-layer-protos.h" #include "app-layer-parser.h" #include "app-layer-ssh.h" +#include "rust.h" #include "conf.h" @@ -52,562 +53,6 @@ #include "util-byte.h" #include "util-memcmp.h" -/** \internal - * \brief Function to parse the SSH version string of the client - * - * The input to this function is a byte buffer starting with SSH- - * - * \param ssh_state Pointer the state in which the value to be stored - * \param input Pointer the received input data - * \param input_len Length in bytes of the received data - * - * \retval len remaining length in input - */ -static int SSHParseBanner(SshState *state, SshHeader *header, const uint8_t *input, uint32_t input_len) -{ - const uint8_t *line_ptr = input; - uint32_t line_len = input_len; - - /* is it the version line? */ - if (line_len < 4) { - SCReturnInt(-1); - } - if (SCMemcmp("SSH-", line_ptr, 4) != 0) { - SCReturnInt(-1); - } - - const uint8_t *banner_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\r", 1); - if (banner_end == NULL) { - banner_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\n", 1); - if (banner_end == NULL) { - SCLogDebug("No EOL at the end of banner buffer"); - SCReturnInt(-1); - } - } - - if ((banner_end - line_ptr) > 255) { - SCLogDebug("Invalid version string, it should be less than 255 " - "characters including , input value is %"PRIuMAX, - (uintmax_t)(banner_end - line_ptr)); - SCReturnInt(-1); - } - - /* don't search things behind the end of banner */ - line_len = banner_end - line_ptr; - - /* ok, we have found the version line/string, skip it and parse proto version */ - line_ptr += 4; - line_len -= 4; - - uint8_t *proto_end = BasicSearch(line_ptr, line_len, (uint8_t*)"-", 1); - if (proto_end == NULL) { - /* Strings starting with SSH- are not allowed - * if they are not the real version string */ - SCLogDebug("Info Version String for SSH (invalid usage of SSH- prefix)"); - SCReturnInt(-1); - } - uint64_t proto_ver_len = (uint64_t)(proto_end - line_ptr); - header->proto_version = SCMalloc(proto_ver_len + 1); - if (header->proto_version == NULL) { - SCReturnInt(-1); - } - memcpy(header->proto_version, line_ptr, proto_ver_len); - header->proto_version[proto_ver_len] = '\0'; - - /* Now lets parse the software & version */ - line_ptr += proto_ver_len + 1; - line_len -= proto_ver_len + 1; - if (line_len < 1) { - SCLogDebug("No software version specified (weird)"); - header->flags |= SSH_FLAG_VERSION_PARSED; - /* Return the remaining length */ - SCReturnInt(0); - } - - uint64_t sw_ver_len = (uint64_t)(banner_end - line_ptr); - /* sanity check on this arithmetic */ - if ((sw_ver_len <= 1) || (sw_ver_len >= input_len)) { - SCLogDebug("Should not have sw version length '%" PRIu64 "'", sw_ver_len); - header->flags |= SSH_FLAG_VERSION_PARSED; - SCReturnInt(-1); - } - - header->software_version = SCMalloc(sw_ver_len + 1); - if (header->software_version == NULL) { - header->flags |= SSH_FLAG_VERSION_PARSED; - SCReturnInt(-1); - } - memcpy(header->software_version, line_ptr, sw_ver_len); - header->software_version[sw_ver_len] = '\0'; - if (header->software_version[sw_ver_len - 1] == 0x0d) - header->software_version[sw_ver_len - 1] = '\0'; - - header->flags |= SSH_FLAG_VERSION_PARSED; - - /* Return the remaining length */ - int len = input_len - (banner_end - input); - SCReturnInt(len); -} - -static int SSHParseRecordHeader(SshState *state, SshHeader *header, - const uint8_t *input, uint32_t input_len) -{ -#ifdef DEBUG - BUG_ON(input_len != 6); -#else - if (input_len < 6) - SCReturnInt(-1); -#endif - /* input and input_len now point past initial line */ - uint32_t pkt_len = 0; - int r = ByteExtractUint32(&pkt_len, BYTE_BIG_ENDIAN, - 4, input); - if (r != 4) { - SCLogDebug("xtract 4 bytes failed %d", r); - SCReturnInt(-1); - } - if (pkt_len < 2) { - SCReturnInt(-1); - } - - header->pkt_len = pkt_len; - SCLogDebug("pkt len: %"PRIu32, pkt_len); - - input += 4; - //input_len -= 4; - - header->padding_len = *input; - - input += 1; - //input_len -= 1; - - SCLogDebug("padding: %u", header->padding_len); - - header->msg_code = *input; - - SCLogDebug("msg code: %u", header->msg_code); - - if (header->msg_code == SSH_MSG_NEWKEYS) { - /* done */ - SCLogDebug("done"); - header->flags |= SSH_FLAG_PARSER_DONE; - } else { - /* not yet done */ - SCLogDebug("not done"); - } - SCReturnInt(0); -} - -/** \internal - * \brief Function to parse the SSH field in packet received from the client - * - * Input to this function is a byte buffer starting with SSH- up to at least - * a \r or \n character. - * - * \param ssh_state Pointer the state in which the value to be stored - * \param input Pointer the received input data - * \param input_len Length in bytes of the received data - */ -static int SSHParseRecord(SshState *state, SshHeader *header, const uint8_t *input, uint32_t input_len) -{ - SCEnter(); - int ret = 0; - - if (header->flags & SSH_FLAG_PARSER_DONE) { - SCReturnInt(0); - } - - SCLogDebug("state %p, input %p,input_len %" PRIu32, - state, input, input_len); - //PrintRawDataFp(stdout, input, input_len); - - if (!(header->flags & SSH_FLAG_VERSION_PARSED)) { - ret = SSHParseBanner(state, header, input, input_len); - if (ret < 0) { - SCLogDebug("Invalid version string"); - SCReturnInt(-1); - } else if (header->flags & SSH_FLAG_VERSION_PARSED) { - SCLogDebug("Version string parsed, remaining length %d", ret); - input += input_len - ret; - input_len -= (input_len - ret); - - uint32_t u = 0; - while (u < input_len && (input[u] == '\r' || input[u] == '\n')) { - u++; - } - SCLogDebug("skipping %u EOL bytes", u); - input += u; - input_len -= u; - - if (input_len == 0) - SCReturnInt(0); - - } else { - BUG_ON(1);// we only call this when we have enough data - SCLogDebug("Version string not parsed yet"); - //pstate->parse_field = 0; - SCReturnInt(0); - } - } else { - SCLogDebug("Version string already parsed"); - } - - /* skip bytes from the current record if we have to */ - if (header->record_left > 0) { - SCLogDebug("skipping bytes part of the current record"); - if (header->record_left > input_len) { - header->record_left -= input_len; - SCLogDebug("all input skipped, %u left in record", header->record_left); - SCReturnInt(0); - } else { - input_len -= header->record_left; - input += header->record_left; - header->record_left = 0; - - if (input_len == 0) { - SCLogDebug("all input skipped"); - SCReturnInt(0); - } - } - } - -again: - /* input is too small, even when combined with stored bytes */ - if (header->buf_offset + input_len < 6) { - memcpy(header->buf + header->buf_offset, input, input_len); - header->buf_offset += input_len; - SCReturnInt(0); - - /* we have enough bytes to parse 6 bytes, lets see if we have - * previously stored some */ - } else if (header->buf_offset > 0) { - uint8_t needed = 6 - header->buf_offset; - - SCLogDebug("parse stored"); - memcpy(header->buf + header->buf_offset, input, needed); - header->buf_offset = 6; - - // parse the 6 - if (SSHParseRecordHeader(state, header, header->buf, 6) < 0) - SCReturnInt(-1); - header->buf_offset = 0; - - uint32_t record_left = header->pkt_len - 2; - input_len -= needed; - input += needed; - - if (record_left > input_len) { - header->record_left = record_left - input_len; - } else { - input_len -= record_left; - if (input_len == 0) - SCReturnInt(0); - - input += record_left; - - SCLogDebug("we have %u left to parse", input_len); - goto again; - - } - - /* nothing stored, lets parse this directly */ - } else { - SCLogDebug("parse direct"); - //PrintRawDataFp(stdout, input, input_len); - if (SSHParseRecordHeader(state, header, input, 6) < 0) - SCReturnInt(-1); - - uint32_t record_left = header->pkt_len - 2; - SCLogDebug("record left %u", record_left); - input_len -= 6; - input += 6; - - if (record_left > input_len) { - header->record_left = record_left - input_len; - } else { - input_len -= record_left; - if (input_len == 0) - SCReturnInt(0); - input += record_left; - //PrintRawDataFp(stdout, input, input_len); - - SCLogDebug("we have %u left to parse", input_len); - goto again; - } - } - - SCReturnInt(0); -} - -static int EnoughData(const uint8_t *input, uint32_t input_len) -{ - uint32_t u; - for (u = 0; u < input_len; u++) { - if (input[u] == '\r' || input[u] == '\n') - return TRUE; - } - return FALSE; -} - -#define MAX_BANNER_LEN 256 - -static int SSHParseData(SshState *state, SshHeader *header, - const uint8_t *input, uint32_t input_len) -{ - /* we're looking for the banner */ - if (!(header->flags & SSH_FLAG_VERSION_PARSED)) - { - int banner_eol = EnoughData(input, input_len); - - /* fast track normal case: no buffering */ - if (header->banner_buffer == NULL && banner_eol) - { - SCLogDebug("enough data, parse now"); - // parse now - int r = SSHParseRecord(state, header, input, input_len); - SCReturnInt(r); - - /* banner EOL with existing buffer present. Time for magic. */ - } else if (banner_eol) { - SCLogDebug("banner EOL with existing buffer"); - - uint32_t tocopy = MAX_BANNER_LEN - header->banner_len; - if (tocopy > input_len) - tocopy = input_len; - - SCLogDebug("tocopy %u input_len %u", tocopy, input_len); - memcpy(header->banner_buffer + header->banner_len, input, tocopy); - header->banner_len += tocopy; - - SCLogDebug("header->banner_len %u", header->banner_len); - int r = SSHParseRecord(state, header, - header->banner_buffer, header->banner_len); - if (r == 0) { - input += tocopy; - input_len -= tocopy; - if (input_len > 0) { - SCLogDebug("handling remaining data %u", input_len); - r = SSHParseRecord(state, header, input, input_len); - } - } - SCReturnInt(r); - - /* no banner EOL, so we need to buffer */ - } else if (!banner_eol) { - if (header->banner_buffer == NULL) { - header->banner_buffer = SCMalloc(MAX_BANNER_LEN); - if (header->banner_buffer == NULL) - SCReturnInt(-1); - } - - uint32_t tocopy = MAX_BANNER_LEN - header->banner_len; - if (tocopy > input_len) - tocopy = input_len; - SCLogDebug("tocopy %u", tocopy); - - memcpy(header->banner_buffer + header->banner_len, input, tocopy); - header->banner_len += tocopy; - SCLogDebug("header->banner_len %u", header->banner_len); - } - - /* we have a banner, the rest is just records */ - } else { - int r = SSHParseRecord(state, header, input, input_len); - SCReturnInt(r); - } - - //PrintRawDataFp(stdout, input, input_len); - return 0; -} - -static AppLayerResult SSHParseRequest(Flow *f, void *state, AppLayerParserState *pstate, - const uint8_t *input, uint32_t input_len, - void *local_data, const uint8_t flags) -{ - SshState *ssh_state = (SshState *)state; - SshHeader *ssh_header = &ssh_state->cli_hdr; - - if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) { - SCReturnStruct(APP_LAYER_OK); - } else if (input == NULL || input_len == 0) { - SCReturnStruct(APP_LAYER_ERROR); - } - - int r = SSHParseData(ssh_state, ssh_header, input, input_len); - - if (ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE && - ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE) { - AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_INSPECTION); - AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_REASSEMBLY); - AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_BYPASS_READY); - } - - if (r < 0) { - SCReturnStruct(APP_LAYER_ERROR); - } - SCReturnStruct(APP_LAYER_OK); -} - -static AppLayerResult SSHParseResponse(Flow *f, void *state, AppLayerParserState *pstate, - const uint8_t *input, uint32_t input_len, - void *local_data, const uint8_t flags) -{ - SshState *ssh_state = (SshState *)state; - SshHeader *ssh_header = &ssh_state->srv_hdr; - - if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) { - SCReturnStruct(APP_LAYER_OK); - } else if (input == NULL || input_len == 0) { - SCReturnStruct(APP_LAYER_ERROR); - } - - int r = SSHParseData(ssh_state, ssh_header, input, input_len); - - if (ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE && - ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE) { - AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_INSPECTION); - AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_REASSEMBLY); - AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_BYPASS_READY); - } - - if (r < 0) { - SCReturnStruct(APP_LAYER_ERROR); - } - SCReturnStruct(APP_LAYER_OK); -} - -/** \brief Function to allocates the SSH state memory - */ -static void *SSHStateAlloc(void) -{ - void *s = SCMalloc(sizeof(SshState)); - if (unlikely(s == NULL)) - return NULL; - - memset(s, 0, sizeof(SshState)); - return s; -} - -/** \brief Function to free the SSH state memory - */ -static void SSHStateFree(void *state) -{ - SshState *s = (SshState *)state; - if (s->cli_hdr.proto_version != NULL) - SCFree(s->cli_hdr.proto_version); - if (s->cli_hdr.software_version != NULL) - SCFree(s->cli_hdr.software_version); - if (s->cli_hdr.banner_buffer != NULL) - SCFree(s->cli_hdr.banner_buffer); - - if (s->srv_hdr.proto_version != NULL) - SCFree(s->srv_hdr.proto_version); - if (s->srv_hdr.software_version != NULL) - SCFree(s->srv_hdr.software_version); - if (s->srv_hdr.banner_buffer != NULL) - SCFree(s->srv_hdr.banner_buffer); - - //AppLayerDecoderEventsFreeEvents(&s->decoder_events); - - if (s->de_state != NULL) { - DetectEngineStateFree(s->de_state); - } - - SCFree(s); -} - -static int SSHSetTxDetectState(void *vtx, DetectEngineState *de_state) -{ - SshState *ssh_state = (SshState *)vtx; - ssh_state->de_state = de_state; - return 0; -} - -static DetectEngineState *SSHGetTxDetectState(void *vtx) -{ - SshState *ssh_state = (SshState *)vtx; - return ssh_state->de_state; -} - -static void SSHStateTransactionFree(void *state, uint64_t tx_id) -{ - /* do nothing */ -} - -static void *SSHGetTx(void *state, uint64_t tx_id) -{ - SshState *ssh_state = (SshState *)state; - return ssh_state; -} - -static uint64_t SSHGetTxCnt(void *state) -{ - /* single tx */ - return 1; -} - -static void SSHSetTxLogged(void *state, void *tx, LoggerId logged) -{ - SshState *ssh_state = (SshState *)state; - if (ssh_state) - ssh_state->logged = logged; -} - -static LoggerId SSHGetTxLogged(void *state, void *tx) -{ - SshState *ssh_state = (SshState *)state; - if (ssh_state) { - return ssh_state->logged; - } - return 0; -} - -static uint64_t SSHGetTxDetectFlags(void *vtx, uint8_t dir) -{ - SshState *ssh_state = (SshState *)vtx; - if (dir & STREAM_TOSERVER) { - return ssh_state->detect_flags_ts; - } else { - return ssh_state->detect_flags_tc; - } -} - -static void SSHSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t flags) -{ - SshState *ssh_state = (SshState *)vtx; - if (dir & STREAM_TOSERVER) { - ssh_state->detect_flags_ts = flags; - } else { - ssh_state->detect_flags_tc = flags; - } -} - -static int SSHGetAlstateProgressCompletionStatus(uint8_t direction) -{ - return SSH_STATE_FINISHED; -} - -static int SSHGetAlstateProgress(void *tx, uint8_t direction) -{ - SshState *ssh_state = (SshState *)tx; - - if (ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE && - ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE) { - return SSH_STATE_FINISHED; - } - - if (direction == STREAM_TOSERVER) { - if (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED) { - return SSH_STATE_BANNER_DONE; - } - } else { - if (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED) { - return SSH_STATE_BANNER_DONE; - } - } - - return SSH_STATE_IN_PROGRESS; -} static int SSHRegisterPatternsForProtocolDetection(void) { @@ -636,36 +81,9 @@ void RegisterSSHParsers(void) return; } - if (AppLayerParserConfParserEnabled("tcp", proto_name)) { - AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SSH, STREAM_TOSERVER, - SSHParseRequest); - AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SSH, STREAM_TOCLIENT, - SSHParseResponse); - AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_SSH, SSHStateAlloc, SSHStateFree); - AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, - ALPROTO_SSH, STREAM_TOSERVER|STREAM_TOCLIENT); - - AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_SSH, SSHStateTransactionFree); - - AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_SSH, - SSHGetTxDetectState, SSHSetTxDetectState); - - AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_SSH, SSHGetTx); + SCLogDebug("Registering Rust SSH parser."); + rs_ssh_register_parser(); - AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_SSH, SSHGetTxCnt); - - AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_SSH, SSHGetAlstateProgress); - - AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_SSH, SSHGetTxLogged, SSHSetTxLogged); - AppLayerParserRegisterDetectFlagsFuncs(IPPROTO_TCP, ALPROTO_SSH, - SSHGetTxDetectFlags, SSHSetTxDetectFlags); - - AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_SSH, - SSHGetAlstateProgressCompletionStatus); - } else { -// SCLogInfo("Parsed disabled for %s protocol. Protocol detection" -// "still on.", proto_name); - } #ifdef UNITTESTS AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SSH, SSHParserRegisterTests); @@ -675,6 +93,49 @@ void RegisterSSHParsers(void) /* UNITTESTS */ #ifdef UNITTESTS #include "flow-util.h" +#include "stream-tcp-util.h" +#include "util-unittest-helper.h" + +static int SSHParserTestUtilCheck(const char *protoexp, const char *softexp, void *tx, uint8_t flags) { + const uint8_t *protocol = NULL; + uint32_t p_len = 0; + const uint8_t *software = NULL; + uint32_t s_len = 0; + + if (rs_ssh_tx_get_protocol(tx, &protocol, &p_len, flags) != 1) { + printf("Version string not parsed correctly return: "); + return 1; + } + if (protocol == NULL) { + printf("Version string not parsed correctly NULL: "); + return 1; + } + + if (p_len != strlen(protoexp)) { + printf("Version string not parsed correctly length: "); + return 1; + } + if (memcmp(protocol, protoexp, strlen(protoexp)) != 0) { + printf("Version string not parsed correctly: "); + return 1; + } + + if (softexp != NULL) { + if (rs_ssh_tx_get_software(tx, &software, &s_len, flags) != 1) + return 1; + if (software == NULL) + return 1; + if (s_len != strlen(softexp)) { + printf("Software string not parsed correctly length: "); + return 1; + } + if (memcmp(software, softexp, strlen(softexp)) != 0) { + printf("Software string not parsed correctly: "); + return 1; + } + } + return 0; +} /** \test Send a version string in one chunk (client version str). */ static int SSHParserTest01(void) @@ -701,36 +162,20 @@ static int SSHParserTest01(void) goto end; } - SshState *ssh_state = f.alstate; + void *ssh_state = f.alstate; if (ssh_state == NULL) { printf("no ssh state: "); goto end; } - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->cli_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->cli_hdr.proto_version == NULL) { + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOSERVER) != SshStateBannerDone ) { printf("Client version string not parsed: "); goto end; } - if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } - - if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); + if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER)) goto end; - } result = 1; end: @@ -768,36 +213,19 @@ static int SSHParserTest02(void) goto end; } - SshState *ssh_state = f.alstate; + void *ssh_state = f.alstate; if (ssh_state == NULL) { printf("no ssh state: "); goto end; } + void * tx = rs_ssh_state_get_tx(ssh_state, 0); - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->cli_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->cli_hdr.proto_version == NULL) { + if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOSERVER) != SshStateBannerDone ) { printf("Client version string not parsed: "); goto end; } - - if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } - - if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); + if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER)) goto end; - } result = 1; end: @@ -835,24 +263,23 @@ static int SSHParserTest03(void) goto end; } - SshState *ssh_state = f.alstate; + void *ssh_state = f.alstate; if (ssh_state == NULL) { printf("no ssh state: "); goto end; } + void * tx = rs_ssh_state_get_tx(ssh_state, 0); - if (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED) { + if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOSERVER) == SshStateBannerDone ) { printf("Client version string parsed? It's not a valid string: "); goto end; } - - if (ssh_state->cli_hdr.proto_version != NULL) { + const uint8_t *dummy = NULL; + uint32_t dummy_len = 0; + if (rs_ssh_tx_get_protocol(tx, &dummy, &dummy_len, STREAM_TOSERVER) != 0) goto end; - } - - if (ssh_state->cli_hdr.software_version != NULL) { + if (rs_ssh_tx_get_software(tx, &dummy, &dummy_len, STREAM_TOSERVER) != 0) goto end; - } result = 1; end: @@ -888,36 +315,19 @@ static int SSHParserTest04(void) goto end; } - SshState *ssh_state = f.alstate; + void *ssh_state = f.alstate; if (ssh_state == NULL) { printf("no ssh state: "); goto end; } + void * tx = rs_ssh_state_get_tx(ssh_state, 0); - if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->srv_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->srv_hdr.proto_version == NULL) { + if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOCLIENT) != SshStateBannerDone ) { printf("Client version string not parsed: "); goto end; } - - if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } - - if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); + if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT)) goto end; - } result = 1; @@ -955,36 +365,19 @@ static int SSHParserTest05(void) goto end; } - SshState *ssh_state = f.alstate; + void *ssh_state = f.alstate; if (ssh_state == NULL) { printf("no ssh state: "); goto end; } + void * tx = rs_ssh_state_get_tx(ssh_state, 0); - if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->srv_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->srv_hdr.proto_version == NULL) { + if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOCLIENT) != SshStateBannerDone ) { printf("Client version string not parsed: "); goto end; } - - if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } - - if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); + if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT)) goto end; - } result = 1; end: @@ -1022,24 +415,24 @@ static int SSHParserTest06(void) } /* Ok, it returned an error. Let's make sure we didn't parse the string at all */ - SshState *ssh_state = f.alstate; + void *ssh_state = f.alstate; if (ssh_state == NULL) { printf("no ssh state: "); goto end; } + void * tx = rs_ssh_state_get_tx(ssh_state, 0); - if (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED) { + if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOCLIENT) == SshStateBannerDone ) { printf("Client version string parsed? It's not a valid string: "); goto end; } - - if (ssh_state->srv_hdr.proto_version != NULL) { + const uint8_t *dummy = NULL; + uint32_t dummy_len = 0; + if (rs_ssh_tx_get_protocol(tx, &dummy, &dummy_len, STREAM_TOCLIENT) != 0) goto end; - } - - if (ssh_state->srv_hdr.software_version != NULL) { + if (rs_ssh_tx_get_software(tx, &dummy, &dummy_len, STREAM_TOCLIENT) != 0) goto end; - } + result = 1; end: @@ -1050,312 +443,208 @@ end: return result; } +#define MAX_SSH_TEST_SIZE 512 + static int SSHParserTest07(void) { - int result = 0; - Flow f; - uint8_t sshbuf1[] = "SSH-2."; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; - uint8_t sshbuf2[] = { "0-MySSHClient-0.5.1\r\n"}; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + Flow *f = NULL; + Packet *p = NULL; - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; + char sshbufs[2][MAX_SSH_TEST_SIZE] = {"SSH-2.", "0-MySSHClient-0.5.1\r\n"}; - StreamTcpInitConfig(TRUE); + memset(&tv, 0x00, sizeof(tv)); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } - - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); - if (ssh_state->cli_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; - if (ssh_state->cli_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; - if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; + uint32_t seq = 2; + for (int i=0; i<2; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1); + seq += strlen(sshbufs[i]); + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0); } - if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOSERVER) != SshStateBannerDone ); - result = 1; + FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER)); -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send a version banner in three chunks. */ static int SSHParserTest08(void) { - int result = 0; - Flow f; - uint8_t sshbuf1[] = "SSH-"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; - uint8_t sshbuf2[] = "2."; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; - uint8_t sshbuf3[] = { "0-MySSHClient-0.5.1\r\n"}; - uint32_t sshlen3 = sizeof(sshbuf3) - 1; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + Flow *f = NULL; + Packet *p = NULL; - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; - - StreamTcpInitConfig(TRUE); + char sshbufs[3][MAX_SSH_TEST_SIZE] = {"SSH-", "2.", "0-MySSHClient-0.5.1\r\n"}; - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } + memset(&tv, 0x00, sizeof(tv)); - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; - if (ssh_state->cli_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; - if (ssh_state->cli_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; + uint32_t seq = 2; + for (int i=0; i<3; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1); + seq += strlen(sshbufs[i]); + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0); } - if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOSERVER) != SshStateBannerDone ); - if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER)); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } static int SSHParserTest09(void) { - int result = 0; - Flow f; - uint8_t sshbuf1[] = "SSH-2."; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; - uint8_t sshbuf2[] = { "0-MySSHClient-0.5.1\r\n"}; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; + Flow *f = NULL; + Packet *p = NULL; - StreamTcpInitConfig(TRUE); - - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOCLIENT, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } + char sshbufs[2][MAX_SSH_TEST_SIZE] = {"SSH-2.", "0-MySSHClient-0.5.1\r\n"}; - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + memset(&tv, 0x00, sizeof(tv)); - if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); - if (ssh_state->srv_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; - if (ssh_state->srv_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; - if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; + uint32_t seq = 2; + for (int i=0; i<2; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1); + seq += strlen(sshbufs[i]); + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOCLIENT) != SshStateBannerDone ); - result = 1; + FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT)); -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send a version banner in three chunks. */ static int SSHParserTest10(void) { - int result = 0; - Flow f; - uint8_t sshbuf1[] = "SSH-"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; - uint8_t sshbuf2[] = "2."; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; - uint8_t sshbuf3[] = { "0-MySSHClient-0.5.1\r\n"}; - uint32_t sshlen3 = sizeof(sshbuf3) - 1; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; + Flow *f = NULL; + Packet *p = NULL; - StreamTcpInitConfig(TRUE); + char sshbufs[3][MAX_SSH_TEST_SIZE] = {"SSH-", "2.", "0-MySSHClient-0.5.1\r\n"}; - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOCLIENT, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } + memset(&tv, 0x00, sizeof(tv)); - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); - if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; - if (ssh_state->srv_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; - if (ssh_state->srv_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; + uint32_t seq = 2; + for (int i=0; i<3; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1); + seq += strlen(sshbufs[i]); + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOCLIENT) != SshStateBannerDone ); - if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT)); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send a banner and record in three chunks. */ @@ -1391,41 +680,18 @@ static int SSHParserTest11(void) goto end; } - SshState *ssh_state = f.alstate; + void *ssh_state = f.alstate; if (ssh_state == NULL) { printf("no ssh state: "); goto end; } - - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->cli_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->cli_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } - - if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } - - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) { + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + if ( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateFinished ) { printf("Didn't detect the msg code of new keys (ciphered data starts): "); goto end; } + if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER)) + goto end; result = 1; end: @@ -1477,41 +743,18 @@ static int SSHParserTest12(void) goto end; } - SshState *ssh_state = f.alstate; + void *ssh_state = f.alstate; if (ssh_state == NULL) { printf("no ssh state: "); goto end; } - - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->cli_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->cli_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } - - if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } - - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) { + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + if ( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateFinished ) { printf("Didn't detect the msg code of new keys (ciphered data starts): "); goto end; } + if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER)) + goto end; result = 1; end: @@ -1525,697 +768,435 @@ end: /** \test Send a banner and 2 records record in four chunks. */ static int SSHParserTest13(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 17}; - uint32_t sshlen2 = sizeof(sshbuf2); uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 21}; - uint32_t sshlen3 = sizeof(sshbuf3); - TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - uint32_t u; - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; + uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3}; + uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3)}; - StreamTcpInitConfig(TRUE); + memset(&tv, 0x00, sizeof(tv)); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - for (u = 0; u < sshlen2; u++) { - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, &sshbuf2[u], 1); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - } - for (u = 0; u < sshlen3; u++) { - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, &sshbuf3[u], 1); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } - } - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } - - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); + + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; + + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; + + uint32_t seq = 2; + for (int i=0; i<3; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0); } - if (ssh_state->cli_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->cli_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } - - if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateFinished ); - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("Didn't detect the msg code of new keys (ciphered data starts): "); - goto end; - } + FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER)); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send a banner and 2 records record in four chunks. */ static int SSHParserTest14(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00}; - uint32_t sshlen2 = sizeof(sshbuf2); - uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - uint32_t sshlen3 = sizeof(sshbuf3); uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00}; - uint32_t sshlen4 = sizeof(sshbuf4); - /* first byte of this record in sshbuf4 */ uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 21}; - uint32_t sshlen5 = sizeof(sshbuf5); - TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; + uint8_t* sshbufs[5] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4, sshbuf5}; + uint32_t sshlens[5] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3), sizeof(sshbuf4), sizeof(sshbuf5)}; - StreamTcpInitConfig(TRUE); + memset(&tv, 0x00, sizeof(tv)); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf5, sshlen5); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; - if (ssh_state->cli_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->cli_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; + uint32_t seq = 2; + for (int i=0; i<5; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0); } - if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateFinished ); - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("Didn't detect the msg code of new keys (ciphered data starts): "); - goto end; - } + FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER)); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send a banner and 2 records record in four chunks. */ static int SSHParserTest15(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00}; - uint32_t sshlen2 = sizeof(sshbuf2); - uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - uint32_t sshlen3 = sizeof(sshbuf3); uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00}; - uint32_t sshlen4 = sizeof(sshbuf4); - - /* first byte of this record in sshbuf4 */ uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 20, 0x00, 0x00, 0x00, 0x02, 0x01, 21}; - uint32_t sshlen5 = sizeof(sshbuf5); - TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; + uint8_t* sshbufs[5] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4, sshbuf5}; + uint32_t sshlens[5] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3), sizeof(sshbuf4), sizeof(sshbuf5)}; - StreamTcpInitConfig(TRUE); + memset(&tv, 0x00, sizeof(tv)); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf5, sshlen5); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; - if (ssh_state->cli_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; + uint32_t seq = 2; + for (int i=0; i<5; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0); } - if (ssh_state->cli_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateFinished ); - if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER)); - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("Didn't detect the msg code of new keys (ciphered data starts): "); - goto end; - } - - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send toserver a banner and record in three chunks. */ static int SSHParserTest16(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n"; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00}; - uint32_t sshlen3 = sizeof(sshbuf3); - TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; - - StreamTcpInitConfig(TRUE); - - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOCLIENT, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } + uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3}; + uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3)}; - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + memset(&tv, 0x00, sizeof(tv)); - if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); - if (ssh_state->srv_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; - if (ssh_state->srv_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; - if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; + uint32_t seq = 2; + for (int i=0; i<3; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished ); - if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("Didn't detect the msg code of new keys (ciphered data starts): "); - goto end; - } + FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT)); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send toserver a banner and 2 records record in four chunks. */ static int SSHParserTest17(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n"; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 17, 0x00}; - uint32_t sshlen3 = sizeof(sshbuf3); uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00}; - uint32_t sshlen4 = sizeof(sshbuf4); - TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; + uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; + uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3), sizeof(sshbuf4)}; - StreamTcpInitConfig(TRUE); + memset(&tv, 0x00, sizeof(tv)); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOCLIENT, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); - goto end; - } + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; - if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; - if (ssh_state->srv_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; + uint32_t seq = 2; + for (int i=0; i<4; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - if (ssh_state->srv_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished ); - if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT)); - if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("Didn't detect the msg code of new keys (ciphered data starts): "); - goto end; - } - - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test 2 directional test */ static int SSHParserTest18(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; uint8_t server1[] = "SSH-2.0-OpenSSH_4.7p1 Debian-8ubuntu3\r\n"; - uint32_t serverlen1 = sizeof(server1) - 1; - uint8_t sshbuf1[] = "SSH-"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n"; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; - uint8_t server2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 }; - uint32_t serverlen2 = sizeof(server2) - 1; - - uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 }; - uint32_t sshlen3 = sizeof(sshbuf3); - - - TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; - - StreamTcpInitConfig(TRUE); - - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOCLIENT, server1, serverlen1); - if (r != 0) { - printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - server2, serverlen2); - if (r != 0) { - printf("toclient chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } - - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } - - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("Didn't detect the msg code of new keys (ciphered data starts): "); - goto end; - } - - if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("Didn't detect the msg code of new keys (ciphered data starts): "); - goto end; - } - - if (!(AppLayerParserStateIssetFlag(f.alparser, APP_LAYER_PARSER_NO_INSPECTION))) { - printf("detection not disabled: "); - goto end; - } - - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; -} - -/** \test Really long banner handling: bannel exactly 255 */ -static int SSHParserTest19(void) -{ - int result = 0; - Flow f; - uint8_t sshbuf1[] = "SSH-"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; - uint8_t sshbuf2[] = "2.0-"; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8 - uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz" - "abcdefghijklmnopqrstuvwxyz"//60 - "abcdefghijklmnopqrstuvwxyz" - "abcdefghijklmnopqrstuvwxyz"//112 - "abcdefghijklmnopqrstuvwxyz" - "abcdefghijklmnopqrstuvwxyz"//164 - "abcdefghijklmnopqrstuvwxyz" - "abcdefghijklmnopqrstuvwxyz"//216 - "abcdefghijklmnopqrstuvwxyz"//242 - "abcdefghijkl\r";//255 - uint32_t sshlen3 = sizeof(sshbuf3) - 1; - - uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00}; - uint32_t sshlen4 = sizeof(sshbuf4); - TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; - - StreamTcpInitConfig(TRUE); - - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOCLIENT, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); - goto end; - } + uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 }; - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } - if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; + memset(&tv, 0x00, sizeof(tv)); + + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); + + uint8_t* sshbufs[5] = {server1, sshbuf1, sshbuf2, server2, sshbuf3}; + uint32_t sshlens[5] = {sizeof(server1) - 1, sizeof(sshbuf1) - 1, sizeof(sshbuf2) -1, sizeof(server2) - 1, sizeof(sshbuf3)}; + bool sshdirs[5] = {true, false, false, true, false}; + + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; + + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; + + uint32_t seqcli = 2; + uint32_t seqsrv = 2; + for (int i=0; i<5; i++) { + if (sshdirs[i]) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seqsrv, sshbufs[i], sshlens[i]) == -1); + seqsrv += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); + } else { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seqcli, sshbufs[i], sshlens[i]) == -1); + seqcli += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0); + } } - if (ssh_state->srv_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished ); - if (ssh_state->srv_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + FAIL_IF(!(AppLayerParserStateIssetFlag(f->alparser, APP_LAYER_PARSER_NO_INSPECTION))); - char *name = SCCalloc(1, 256); - if (name == NULL) - goto end; - strlcpy(name, (char *)sshbuf3, 256); - name[strlen(name) - 1] = '\0'; // strip \r + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; +} - if (strcmp((char*)ssh_state->srv_hdr.software_version, name) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } +/** \test Really long banner handling: bannel exactly 255 */ +static int SSHParserTest19(void) +{ + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; - if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + uint8_t sshbuf1[] = "SSH-"; + uint8_t sshbuf2[] = "2.0-"; + uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz"//60 + "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz"//112 + "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz"//164 + "abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz"//216 + "abcdefghijklmnopqrstuvwxyz"//242 + "abcdefghijkl\r";//255 + uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00}; - if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("Didn't detect the msg code of new keys (ciphered data starts): "); - goto end; + uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; + uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4)}; + + memset(&tv, 0x00, sizeof(tv)); + + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); + + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; + + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; + + uint32_t seq = 2; + for (int i=0; i<4; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished ); + + sshbuf3[sizeof(sshbuf3) - 2] = 0; + FAIL_IF(SSHParserTestUtilCheck("2.0", (char *)sshbuf3, tx, STREAM_TOCLIENT)); + + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Really long banner handling: banner exactly 255, * followed by malformed record */ static int SSHParserTest20(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "2.0-"; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8 uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"//60 "abcdefghijklmnopqrstuvwxyz" @@ -2226,97 +1207,64 @@ static int SSHParserTest20(void) "abcdefghijklmnopqrstuvwxyz"//216 "abcdefghijklmnopqrstuvwxyz"//242 "abcdefghijklm\r";//256 - uint32_t sshlen3 = sizeof(sshbuf3) - 1; uint8_t sshbuf4[] = {'a','b','c','d','e','f', '\r', 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00}; - uint32_t sshlen4 = sizeof(sshbuf4) - 1; - - TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; + uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; + uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; - StreamTcpInitConfig(TRUE); + memset(&tv, 0x00, sizeof(tv)); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOCLIENT, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } - SCLogDebug("chunk 4:"); - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); - goto end; - } + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; - if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; - if (ssh_state->srv_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; + uint32_t seq = 2; + for (int i=0; i<4; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - if (ssh_state->srv_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished ); - if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + FAIL_IF(SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOCLIENT)); - if ((ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("detected the msg code of new keys (ciphered data starts): "); - goto end; - } - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Fragmented banner handling: chunk has final part of bannel plus * a record. */ static int SSHParserTest21(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "2.0-"; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8 uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"//60 "abcdefghijklmnopqrstuvwxyz" @@ -2326,97 +1274,64 @@ static int SSHParserTest21(void) "abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"//216 "abcdefghijklmnopqrstuvwxy";//241 - uint32_t sshlen3 = sizeof(sshbuf3) - 1; uint8_t sshbuf4[] = {'l','i','b','s','s','h', '\r', 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00}; - uint32_t sshlen4 = sizeof(sshbuf4) - 1; - TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; - StreamTcpInitConfig(TRUE); + uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; + uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4)}; - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOCLIENT, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } - SCLogDebug("chunk 4:"); - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); - goto end; - } + memset(&tv, 0x00, sizeof(tv)); - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); - if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; - if (ssh_state->srv_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; - if (ssh_state->srv_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; + uint32_t seq = 2; + for (int i=0; i<4; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished ); - if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("Didn't detect the msg code of new keys (ciphered data starts): "); - goto end; - } + FAIL_IF(SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOCLIENT)); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Fragmented banner handling: chunk has final part of bannel plus * a record. */ static int SSHParserTest22(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "2.0-"; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8 uint8_t sshbuf3[] = { 'l', 'i', 'b', 's', 's', 'h', '\r', //7 @@ -2439,99 +1354,66 @@ static int SSHParserTest22(void) 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //150 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //200 - - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //250 - - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00, 0x00, //300 - }; - uint32_t sshlen3 = sizeof(sshbuf3) - 1; - TcpSession ssn; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - f.alproto = ALPROTO_SSH; - - StreamTcpInitConfig(TRUE); - - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOCLIENT, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - goto end; - } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - goto end; - } -#if 0 - SCLogDebug("chunk 4:"); - r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); - goto end; - } -#endif - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } - - if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->srv_hdr.software_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } - - if (ssh_state->srv_hdr.proto_version == NULL) { - printf("Client version string not parsed: "); - goto end; - } + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //200 + + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //250 + + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00, 0x00, //300 + }; + + + uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3}; + uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1}; + + memset(&tv, 0x00, sizeof(tv)); + + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); + + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; + + p = PacketGetFromAlloc(); + FAIL_IF(unlikely(p == NULL)); + p->proto = IPPROTO_TCP; + p->flow = f; + + uint32_t seq = 2; + for (int i=0; i<3; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); + } - if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { - printf("Client version string not parsed correctly: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished ); - if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) { - printf("Didn't detect the msg code of new keys (ciphered data starts): "); - goto end; - } + FAIL_IF(SSHParserTestUtilCheck("2.0", "libssh", tx, STREAM_TOCLIENT)); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send a version string in one chunk (client version str). */ @@ -2593,21 +1475,18 @@ static int SSHParserTest24(void) goto end; } - SshState *ssh_state = f.alstate; + void *ssh_state = f.alstate; if (ssh_state == NULL) { printf("no ssh state: "); goto end; } - - if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - printf("Client version string not parsed: "); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + if ( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateBannerDone ) { + printf("Didn't detect the msg code of new keys (ciphered data starts): "); goto end; } - - if (ssh_state->cli_hdr.software_version) { - printf("Client version string should not be parsed: "); + if (SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOSERVER)) goto end; - } result = 1; end: @@ -2640,11 +1519,13 @@ static int SSHParserTest25(void) STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen); FAIL_IF(r != -1); - SshState *ssh_state = f.alstate; + void *ssh_state = f.alstate; FAIL_IF_NULL(ssh_state); - - FAIL_IF_NOT(!(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)); - FAIL_IF(ssh_state->cli_hdr.software_version); + void * tx = rs_ssh_state_get_tx(ssh_state, 0); + FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) == SshStateBannerDone ); + const uint8_t *dummy = NULL; + uint32_t dummy_len = 0; + FAIL_IF (rs_ssh_tx_get_software(tx, &dummy, &dummy_len, STREAM_TOCLIENT) != 0); AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); diff --git a/src/app-layer-ssh.h b/src/app-layer-ssh.h index ef988b3c0d..119ae03283 100644 --- a/src/app-layer-ssh.h +++ b/src/app-layer-ssh.h @@ -25,63 +25,6 @@ #ifndef __APP_LAYER_SSH_H__ #define __APP_LAYER_SSH_H__ -/* header flag */ -#define SSH_FLAG_VERSION_PARSED 0x01 - -/* This flags indicate that the rest of the communication - * must be ciphered, so the parsing finish here */ -#define SSH_FLAG_PARSER_DONE 0x02 - -/* MSG_CODE */ -#define SSH_MSG_NEWKEYS 21 - -/** From SSH-TRANSP rfc - - SSH Bunary packet structure: - uint32 packet_length - byte padding_length - byte[n1] payload; n1 = packet_length - padding_length - 1 - byte[n2] random padding; n2 = padding_length - byte[m] mac (Message Authentication Code - MAC); m = mac_length - - So we are going to do a header struct to store - the lenghts and msg_code (inside payload, if any) -*/ - -typedef struct SshHeader_ { - uint32_t pkt_len; - uint8_t padding_len; - uint8_t msg_code; - uint16_t banner_len; - uint8_t buf[6]; - uint8_t buf_offset; - uint8_t flags; - uint32_t record_left; - uint8_t *proto_version; - uint8_t *software_version; - uint8_t *banner_buffer; -} SshHeader; - -enum { - SSH_STATE_IN_PROGRESS, - SSH_STATE_BANNER_DONE, - SSH_STATE_FINISHED, -}; - -/** structure to store the SSH state values */ -typedef struct SshState_ { - SshHeader srv_hdr; - SshHeader cli_hdr; - - /* specifies which loggers are done logging */ - uint32_t logged; - - uint64_t detect_flags_ts; - uint64_t detect_flags_tc; - - DetectEngineState *de_state; -} SshState; - void RegisterSSHParsers(void); void SSHParserRegisterTests(void); diff --git a/src/detect-ssh-proto-version.c b/src/detect-ssh-proto-version.c index f9d8ae5728..30a503ff2c 100644 --- a/src/detect-ssh-proto-version.c +++ b/src/detect-ssh-proto-version.c @@ -51,6 +51,7 @@ #include "app-layer-parser.h" #include "app-layer-ssh.h" #include "detect-ssh-proto-version.h" +#include "rust.h" #include "stream-tcp.h" @@ -109,34 +110,35 @@ static int DetectSshVersionMatch (DetectEngineThreadCtx *det_ctx, SCLogDebug("lets see"); DetectSshVersionData *ssh = (DetectSshVersionData *)m; - SshState *ssh_state = (SshState *)state; - if (ssh_state == NULL) { + if (state == NULL) { SCLogDebug("no ssh state, no match"); SCReturnInt(0); } int ret = 0; - if ((flags & STREAM_TOCLIENT) && (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) { - SCLogDebug("looking for ssh server protoversion 2 compat"); - if (strncmp((char *) ssh_state->srv_hdr.proto_version, "2", 1) == 0 || - strncmp((char *) ssh_state->srv_hdr.proto_version, "2.", 2) == 0 || - strncmp((char *) ssh_state->srv_hdr.proto_version, "1.99", 4) == 0) + const uint8_t *protocol = NULL; + uint32_t b_len = 0; + + if (rs_ssh_tx_get_protocol(txv, &protocol, &b_len, flags) != 1) + SCReturnInt(0); + if (protocol == NULL || b_len == 0) + SCReturnInt(0); + + if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) { + SCLogDebug("looking for ssh protoversion 2 compat"); + if (protocol[0] == '2') { + ret = 1; + } else if (b_len >= 4) { + if (memcmp(protocol, "1.99", 4) == 0) { ret = 1; - } else { - SCLogDebug("looking for ssh server protoversion %s length %"PRIu16"", ssh->ver, ssh->len); - ret = (strncmp((char *) ssh_state->srv_hdr.proto_version, (char *) ssh->ver, ssh->len) == 0)? 1 : 0; + } } - } else if ((flags & STREAM_TOSERVER) && (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) { - SCLogDebug("looking for client ssh client protoversion 2 compat"); - if (strncmp((char *) ssh_state->cli_hdr.proto_version, "2", 1) == 0 || - strncmp((char *) ssh_state->cli_hdr.proto_version, "2.", 2) == 0 || - strncmp((char *) ssh_state->cli_hdr.proto_version, "1.99", 4) == 0) + } else { + SCLogDebug("looking for ssh protoversion %s length %"PRIu16"", ssh->ver, ssh->len); + if (b_len == ssh->len) { + if (memcmp(protocol, ssh->ver, ssh->len) == 0) { ret = 1; - } else { - SCLogDebug("looking for ssh client protoversion %s length %"PRIu16"", ssh->ver, ssh->len); - ret = (strncmp((char *) ssh_state->cli_hdr.proto_version, (char *) ssh->ver, ssh->len) == 0)? 1 : 0; + } } } SCReturnInt(ret); @@ -331,46 +333,52 @@ static int DetectSshVersionTestParse03 (void) #include "stream-tcp-reassemble.h" +#include "stream-tcp-util.h" /** \test Send a get request in three chunks + more data. */ static int DetectSshVersionTestDetect01(void) { - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-1."; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "10-PuTTY_2.123" ; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; uint8_t sshbuf3[] = "\n"; - uint32_t sshlen3 = sizeof(sshbuf3) - 1; uint8_t sshbuf4[] = "whatever..."; - uint32_t sshlen4 = sizeof(sshbuf4) - 1; - TcpSession ssn; - Packet *p = NULL; - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&th_v, 0, sizeof(th_v)); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); + uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; + uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; + + memset(&tv, 0x00, sizeof(tv)); + + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); + + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); - FAIL_IF_NULL(p); + FAIL_IF(unlikely(p == NULL)); + p->flow = f; - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - p->flow = &f; - p->flowflags |= FLOW_PKT_TOSERVER; - p->flowflags |= FLOW_PKT_ESTABLISHED; - p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; - f.alproto = ALPROTO_SSH; - f.proto = IPPROTO_TCP; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; - StreamTcpInitConfig(TRUE); + memset(&th_v, 0, sizeof(th_v)); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL (de_ctx); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:1.10; sid:1;)"); @@ -379,276 +387,183 @@ static int DetectSshVersionTestDetect01(void) SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - SCLogDebug("==> 1"); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - FAIL_IF(r != 0); - - SCLogDebug("==> 2"); - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - FAIL_IF(r != 0); - - SCLogDebug("==> 3"); - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf3, sshlen3); - FAIL_IF(r != 0); - - SCLogDebug("==> 4"); - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf4, sshlen4); - FAIL_IF(r != 0); + uint32_t seq = 2; + for (int i=0; i<4; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); + } - SshState *ssh_state = f.alstate; + void *ssh_state = f->alstate; FAIL_IF_NULL(ssh_state); /* do detect */ SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - FAIL_IF(!(PacketAlertCheck(p, 1))); + FAIL_IF(PacketAlertCheck(p, 1)); DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - UTHFreePackets(&p, 1); - AppLayerParserThreadCtxFree(alp_tctx); - + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); PASS; } /** \test Send a get request in three chunks + more data. */ static int DetectSshVersionTestDetect02(void) { - int result = 0; - Flow f; - uint8_t sshbuf1[] = "SSH-1."; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; - uint8_t sshbuf2[] = "99-PuTTY_2.123" ; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; - uint8_t sshbuf3[] = "\n"; - uint32_t sshlen3 = sizeof(sshbuf3) - 1; - uint8_t sshbuf4[] = "whatever..."; - uint32_t sshlen4 = sizeof(sshbuf4) - 1; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; TcpSession ssn; + Flow *f = NULL; Packet *p = NULL; - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&th_v, 0, sizeof(th_v)); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); + uint8_t sshbuf1[] = "SSH-1.99-Pu"; + uint8_t sshbuf2[] = "TTY_2.123" ; + uint8_t sshbuf3[] = "\n"; + uint8_t sshbuf4[] = "whatever..."; + + uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; + uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; + + memset(&tv, 0x00, sizeof(tv)); + + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); + + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + FAIL_IF(unlikely(p == NULL)); + p->flow = f; - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - p->flow = &f; - p->flowflags |= FLOW_PKT_TOSERVER; - p->flowflags |= FLOW_PKT_ESTABLISHED; - p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; - f.alproto = ALPROTO_SSH; - f.proto = IPPROTO_TCP; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; - StreamTcpInitConfig(TRUE); + memset(&th_v, 0, sizeof(th_v)); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) { - goto end; - } + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)"); - if (s == NULL) { - goto end; - } + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; + uint32_t seq = 2; + for (int i=0; i<4; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - FLOWLOCK_UNLOCK(&f); - - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); /* do detect */ SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - if ( !(PacketAlertCheck(p, 1))) { - printf("Error, the sig should match: "); - goto end; - } - - result = 1; -end: - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); + FAIL_IF(PacketAlertCheck(p, 1)); DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - - UTHFreePackets(&p, 1); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send a get request in three chunks + more data. */ static int DetectSshVersionTestDetect03(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-1."; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "7-PuTTY_2.123" ; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; uint8_t sshbuf3[] = "\n"; - uint32_t sshlen3 = sizeof(sshbuf3) - 1; uint8_t sshbuf4[] = "whatever..."; - uint32_t sshlen4 = sizeof(sshbuf4) - 1; - TcpSession ssn; - Packet *p = NULL; - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&th_v, 0, sizeof(th_v)); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); + uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; + uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; + + memset(&tv, 0x00, sizeof(tv)); + + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); + + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + FAIL_IF(unlikely(p == NULL)); + p->flow = f; - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - p->flow = &f; - p->flowflags |= FLOW_PKT_TOSERVER; - p->flowflags |= FLOW_PKT_ESTABLISHED; - p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; - f.alproto = ALPROTO_SSH; - f.proto = IPPROTO_TCP; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; - StreamTcpInitConfig(TRUE); + memset(&th_v, 0, sizeof(th_v)); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) { - goto end; - } + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)"); - if (s == NULL) { - goto end; - } + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; + uint32_t seq = 2; + for (int i=0; i<4; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - FLOWLOCK_UNLOCK(&f); - - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); /* do detect */ SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - if (PacketAlertCheck(p, 1)) { - printf("Error, 1.7 version is not 2 compat, so the sig should not match: "); - goto end; - } - - result = 1; -end: - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); + FAIL_IF(PacketAlertCheck(p, 1)); DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - - UTHFreePackets(&p, 1); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } #endif /* UNITTESTS */ diff --git a/src/detect-ssh-proto.c b/src/detect-ssh-proto.c index 2f0de667fc..d8eec71d4e 100644 --- a/src/detect-ssh-proto.c +++ b/src/detect-ssh-proto.c @@ -46,6 +46,7 @@ #include "app-layer-parser.h" #include "app-layer-ssh.h" #include "detect-ssh-proto.h" +#include "rust.h" #define KEYWORD_NAME "ssh.proto" #define KEYWORD_NAME_LEGACY "ssh_proto" @@ -63,27 +64,17 @@ static InspectionBuffer *GetSshData(DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); if (buffer->inspect == NULL) { - uint8_t *protocol = NULL; - SshState *ssh_state = (SshState *) txv; + const uint8_t *protocol = NULL; + uint32_t b_len = 0; - if (flow_flags & STREAM_TOSERVER) - protocol = ssh_state->cli_hdr.proto_version; - else if (flow_flags & STREAM_TOCLIENT) - protocol = ssh_state->srv_hdr.proto_version; - - if (protocol == NULL) { - SCLogDebug("SSL protocol not set"); + if (rs_ssh_tx_get_protocol(txv, &protocol, &b_len, flow_flags) != 1) return NULL; - } - - uint32_t data_len = strlen((char *)protocol); - uint8_t *data = protocol; - if (data == NULL || data_len == 0) { - SCLogDebug("SSL protocol not present"); + if (protocol == NULL || b_len == 0) { + SCLogDebug("SSH protocol not set"); return NULL; } - InspectionBufferSetup(buffer, data, data_len); + InspectionBufferSetup(buffer, protocol, b_len); InspectionBufferApplyTransforms(buffer, transforms); } @@ -113,16 +104,16 @@ void DetectSshProtocolRegister(void) DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, GetSshData, - ALPROTO_SSH, SSH_STATE_BANNER_DONE), + ALPROTO_SSH, SshStateBannerDone), DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, GetSshData, - ALPROTO_SSH, SSH_STATE_BANNER_DONE), + ALPROTO_SSH, SshStateBannerDone), DetectAppLayerInspectEngineRegister2(BUFFER_NAME, - ALPROTO_SSH, SIG_FLAG_TOSERVER, SSH_STATE_BANNER_DONE, + ALPROTO_SSH, SIG_FLAG_TOSERVER, SshStateBannerDone, DetectEngineInspectBufferGeneric, GetSshData); DetectAppLayerInspectEngineRegister2(BUFFER_NAME, - ALPROTO_SSH, SIG_FLAG_TOCLIENT, SSH_STATE_BANNER_DONE, + ALPROTO_SSH, SIG_FLAG_TOCLIENT, SshStateBannerDone, DetectEngineInspectBufferGeneric, GetSshData); DetectBufferTypeSetDescriptionByName(BUFFER_NAME, BUFFER_DESC); diff --git a/src/detect-ssh-software-version.c b/src/detect-ssh-software-version.c index d5736e8409..e24a241910 100644 --- a/src/detect-ssh-software-version.c +++ b/src/detect-ssh-software-version.c @@ -55,6 +55,7 @@ #include "app-layer-parser.h" #include "app-layer-ssh.h" #include "detect-ssh-software-version.h" +#include "rust.h" #include "stream-tcp.h" @@ -103,10 +104,10 @@ void DetectSshSoftwareVersionRegister(void) g_ssh_banner_list_id = DetectBufferTypeRegister("ssh_banner"); DetectAppLayerInspectEngineRegister("ssh_banner", - ALPROTO_SSH, SIG_FLAG_TOSERVER, SSH_STATE_BANNER_DONE, + ALPROTO_SSH, SIG_FLAG_TOSERVER, SshStateBannerDone, InspectSshBanner); DetectAppLayerInspectEngineRegister("ssh_banner", - ALPROTO_SSH, SIG_FLAG_TOCLIENT, SSH_STATE_BANNER_DONE, + ALPROTO_SSH, SIG_FLAG_TOCLIENT, SshStateBannerDone, InspectSshBanner); } @@ -128,20 +129,25 @@ static int DetectSshSoftwareVersionMatch (DetectEngineThreadCtx *det_ctx, SCEnter(); DetectSshSoftwareVersionData *ssh = (DetectSshSoftwareVersionData *)m; - SshState *ssh_state = (SshState *)state; - if (ssh_state == NULL) { + if (state == NULL) { SCLogDebug("no ssh state, no match"); SCReturnInt(0); } int ret = 0; - if ((flags & STREAM_TOCLIENT) && (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - SCLogDebug("looking for ssh server softwareversion %s length %"PRIu16" on %s", ssh->software_ver, ssh->len, ssh_state->srv_hdr.software_version); - ret = (strncmp((char *) ssh_state->srv_hdr.software_version, (char *) ssh->software_ver, ssh->len) == 0)? 1 : 0; - } else if ((flags & STREAM_TOSERVER) && (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { - SCLogDebug("looking for ssh client softwareversion %s length %"PRIu16" on %s", ssh->software_ver, ssh->len, ssh_state->cli_hdr.software_version); - ret = (strncmp((char *) ssh_state->cli_hdr.software_version, (char *) ssh->software_ver, ssh->len) == 0)? 1 : 0; + const uint8_t *software = NULL; + uint32_t b_len = 0; + + if (rs_ssh_tx_get_software(txv, &software, &b_len, flags) != 1) + SCReturnInt(0); + if (software == NULL || b_len == 0) + SCReturnInt(0); + if (b_len == ssh->len) { + if (memcmp(software, ssh->software_ver, ssh->len) == 0) { + ret = 1; + } } + SCReturnInt(ret); } @@ -312,357 +318,237 @@ static int DetectSshSoftwareVersionTestParse03 (void) #include "stream-tcp-reassemble.h" +#include "stream-tcp-util.h" /** \test Send a get request in three chunks + more data. */ static int DetectSshSoftwareVersionTestDetect01(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-1."; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "10-PuTTY_2.123" ; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; uint8_t sshbuf3[] = "\n"; - uint32_t sshlen3 = sizeof(sshbuf3) - 1; uint8_t sshbuf4[] = "whatever..."; - uint32_t sshlen4 = sizeof(sshbuf4) - 1; - TcpSession ssn; - Packet *p = NULL; - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&th_v, 0, sizeof(th_v)); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); + uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; + uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; + + memset(&tv, 0x00, sizeof(tv)); + + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); + + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + FAIL_IF(unlikely(p == NULL)); + p->flow = f; - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - p->flow = &f; - p->flowflags |= FLOW_PKT_TOSERVER; - p->flowflags |= FLOW_PKT_ESTABLISHED; - p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; - f.alproto = ALPROTO_SSH; - f.proto = IPPROTO_TCP; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; - StreamTcpInitConfig(TRUE); + memset(&th_v, 0, sizeof(th_v)); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) { - goto end; - } + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:PuTTY_2.123; sid:1;)"); - if (s == NULL) { - goto end; - } + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; + uint32_t seq = 2; + for (int i=0; i<4; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - FLOWLOCK_UNLOCK(&f); - - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); /* do detect */ SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - if ( !(PacketAlertCheck(p, 1))) { - printf("Error, the sig should match: "); - goto end; - } - - result = 1; -end: - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); + FAIL_IF(PacketAlertCheck(p, 1)); DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - - UTHFreePackets(&p, 1); - - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send a get request in three chunks + more data. */ static int DetectSshSoftwareVersionTestDetect02(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-1.99-Pu"; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "TTY_2.123" ; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; uint8_t sshbuf3[] = "\n"; - uint32_t sshlen3 = sizeof(sshbuf3) - 1; uint8_t sshbuf4[] = "whatever..."; - uint32_t sshlen4 = sizeof(sshbuf4) - 1; - TcpSession ssn; - Packet *p = NULL; - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&th_v, 0, sizeof(th_v)); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); + uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; + uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; + + memset(&tv, 0x00, sizeof(tv)); + + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); + + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + FAIL_IF(unlikely(p == NULL)); + p->flow = f; - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - p->flow = &f; - p->flowflags |= FLOW_PKT_TOSERVER; - p->flowflags |= FLOW_PKT_ESTABLISHED; - p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; - f.alproto = ALPROTO_SSH; - f.proto = IPPROTO_TCP; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; - StreamTcpInitConfig(TRUE); + memset(&th_v, 0, sizeof(th_v)); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) { - goto end; - } + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:PuTTY_2.123; sid:1;)"); - if (s == NULL) { - goto end; - } + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; + uint32_t seq = 2; + for (int i=0; i<4; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - FLOWLOCK_UNLOCK(&f); - - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); /* do detect */ SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - if ( !(PacketAlertCheck(p, 1))) { - printf("Error, the sig should match: "); - goto end; - } - - result = 1; -end: - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); + FAIL_IF(PacketAlertCheck(p, 1)); DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - - UTHFreePackets(&p, 1); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } /** \test Send a get request in three chunks + more data. */ static int DetectSshSoftwareVersionTestDetect03(void) { - int result = 0; - Flow f; + TcpReassemblyThreadCtx *ra_ctx = NULL; + ThreadVars tv; + TcpSession ssn; + Flow *f = NULL; + Packet *p = NULL; + uint8_t sshbuf1[] = "SSH-1."; - uint32_t sshlen1 = sizeof(sshbuf1) - 1; uint8_t sshbuf2[] = "7-PuTTY_2.123" ; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; uint8_t sshbuf3[] = "\n"; - uint32_t sshlen3 = sizeof(sshbuf3) - 1; uint8_t sshbuf4[] = "whatever..."; - uint32_t sshlen4 = sizeof(sshbuf4) - 1; - TcpSession ssn; - Packet *p = NULL; - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - memset(&th_v, 0, sizeof(th_v)); - memset(&f, 0, sizeof(f)); - memset(&ssn, 0, sizeof(ssn)); + uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; + uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; + + memset(&tv, 0x00, sizeof(tv)); + + StreamTcpUTInit(&ra_ctx); + StreamTcpUTInitInline(); + StreamTcpUTSetupSession(&ssn); + StreamTcpUTSetupStream(&ssn.server, 1); + StreamTcpUTSetupStream(&ssn.client, 1); + + f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); + FAIL_IF_NULL(f); + f->protoctx = &ssn; + f->proto = IPPROTO_TCP; + f->alproto = ALPROTO_SSH; p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + FAIL_IF(unlikely(p == NULL)); + p->flow = f; - FLOW_INITIALIZE(&f); - f.protoctx = (void *)&ssn; - p->flow = &f; - p->flowflags |= FLOW_PKT_TOSERVER; - p->flowflags |= FLOW_PKT_ESTABLISHED; - p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; - f.alproto = ALPROTO_SSH; - f.proto = IPPROTO_TCP; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; - StreamTcpInitConfig(TRUE); + memset(&th_v, 0, sizeof(th_v)); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) { - goto end; - } + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:lalala-3.1.4; sid:1;)"); - if (s == NULL) { - goto end; - } + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, - STREAM_TOSERVER, sshbuf1, sshlen1); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf2, sshlen2); - if (r != 0) { - printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; + uint32_t seq = 2; + for (int i=0; i<4; i++) { + FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); + seq += sshlens[i]; + FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); } - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf3, sshlen3); - if (r != 0) { - printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - - r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, - sshbuf4, sshlen4); - if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } - FLOWLOCK_UNLOCK(&f); - - SshState *ssh_state = f.alstate; - if (ssh_state == NULL) { - printf("no ssh state: "); - goto end; - } + void *ssh_state = f->alstate; + FAIL_IF_NULL(ssh_state); /* do detect */ SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - if (PacketAlertCheck(p, 1)) { - printf("Error, 1.7 version is not 2 compat, so the sig should not match: "); - goto end; - } - - result = 1; -end: - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); + FAIL_IF(PacketAlertCheck(p, 1)); DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - StreamTcpFreeConfig(TRUE); - FLOW_DESTROY(&f); - - UTHFreePackets(&p, 1); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - return result; + UTHFreePacket(p); + StreamTcpUTClearSession(&ssn); + StreamTcpUTDeinit(ra_ctx); + UTHFreeFlow(f); + PASS; } #endif /* UNITTESTS */ diff --git a/src/detect-ssh-software.c b/src/detect-ssh-software.c index 4d1b8d9eb1..9765e3844e 100644 --- a/src/detect-ssh-software.c +++ b/src/detect-ssh-software.c @@ -46,6 +46,7 @@ #include "app-layer-parser.h" #include "app-layer-ssh.h" #include "detect-ssh-software.h" +#include "rust.h" #define KEYWORD_NAME "ssh.software" #define KEYWORD_NAME_LEGACY "ssh_software" @@ -63,27 +64,17 @@ static InspectionBuffer *GetSshData(DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); if (buffer->inspect == NULL) { - uint8_t *software = NULL; - SshState *ssh_state = (SshState *) txv; + const uint8_t *software = NULL; + uint32_t b_len = 0; - if (flow_flags & STREAM_TOSERVER) - software = ssh_state->cli_hdr.software_version; - else if (flow_flags & STREAM_TOCLIENT) - software = ssh_state->srv_hdr.software_version; - - if (software == NULL) { - SCLogDebug("SSL software version not set"); + if (rs_ssh_tx_get_software(txv, &software, &b_len, flow_flags) != 1) return NULL; - } - - uint32_t data_len = strlen((char *)software); - uint8_t *data = software; - if (data == NULL || data_len == 0) { - SCLogDebug("SSL software version not present"); + if (software == NULL || b_len == 0) { + SCLogDebug("SSH software version not set"); return NULL; } - InspectionBufferSetup(buffer, data, data_len); + InspectionBufferSetup(buffer, software, b_len); InspectionBufferApplyTransforms(buffer, transforms); } @@ -113,16 +104,16 @@ void DetectSshSoftwareRegister(void) DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, GetSshData, - ALPROTO_SSH, SSH_STATE_BANNER_DONE), + ALPROTO_SSH, SshStateBannerDone), DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, GetSshData, - ALPROTO_SSH, SSH_STATE_BANNER_DONE), + ALPROTO_SSH, SshStateBannerDone), DetectAppLayerInspectEngineRegister2(BUFFER_NAME, - ALPROTO_SSH, SIG_FLAG_TOSERVER, SSH_STATE_BANNER_DONE, + ALPROTO_SSH, SIG_FLAG_TOSERVER, SshStateBannerDone, DetectEngineInspectBufferGeneric, GetSshData); DetectAppLayerInspectEngineRegister2(BUFFER_NAME, - ALPROTO_SSH, SIG_FLAG_TOCLIENT, SSH_STATE_BANNER_DONE, + ALPROTO_SSH, SIG_FLAG_TOCLIENT, SshStateBannerDone, DetectEngineInspectBufferGeneric, GetSshData); DetectBufferTypeSetDescriptionByName(BUFFER_NAME, BUFFER_DESC); diff --git a/src/output-json-alert.c b/src/output-json-alert.c index e72c1701c3..13406df157 100644 --- a/src/output-json-alert.c +++ b/src/output-json-alert.c @@ -61,6 +61,7 @@ #include "output-json-http.h" #include "output-json-tls.h" #include "output-json-ssh.h" +#include "rust.h" #include "output-json-smtp.h" #include "output-json-email-common.h" #include "output-json-nfs.h" @@ -145,14 +146,13 @@ static void AlertJsonTls(const Flow *f, json_t *js) static void AlertJsonSsh(const Flow *f, json_t *js) { - SshState *ssh_state = (SshState *)FlowGetAppState(f); + void *ssh_state = FlowGetAppState(f); if (ssh_state) { - json_t *tjs = json_object(); + void *tx_ptr = rs_ssh_state_get_tx(ssh_state, 0); + json_t *tjs = rs_ssh_log_json(tx_ptr); if (unlikely(tjs == NULL)) return; - JsonSshLogJSON(tjs, ssh_state); - json_object_set_new(js, "ssh", tjs); } diff --git a/src/output-json-ssh.c b/src/output-json-ssh.c index 348cae8359..9906e88cc9 100644 --- a/src/output-json-ssh.c +++ b/src/output-json-ssh.c @@ -49,6 +49,7 @@ #include "output-json.h" #include "output-json-ssh.h" +#include "rust.h" #define MODULE_NAME "LogSshLog" @@ -64,62 +65,30 @@ typedef struct JsonSshLogThread_ { } JsonSshLogThread; -void JsonSshLogJSON(json_t *tjs, SshState *ssh_state) -{ - json_t *cjs = json_object(); - if (cjs != NULL) { - json_object_set_new(cjs, "proto_version", - SCJsonString((char *)ssh_state->cli_hdr.proto_version)); - - json_object_set_new(cjs, "software_version", - SCJsonString((char *)ssh_state->cli_hdr.software_version)); - } - json_object_set_new(tjs, "client", cjs); - - json_t *sjs = json_object(); - if (sjs != NULL) { - json_object_set_new(sjs, "proto_version", - SCJsonString((char *)ssh_state->srv_hdr.proto_version)); - - json_object_set_new(sjs, "software_version", - SCJsonString((char *)ssh_state->srv_hdr.software_version)); - } - json_object_set_new(tjs, "server", sjs); - -} - static int JsonSshLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, void *txptr, uint64_t tx_id) { JsonSshLogThread *aft = (JsonSshLogThread *)thread_data; OutputSshCtx *ssh_ctx = aft->sshlog_ctx; - SshState *ssh_state = (SshState *)state; - if (unlikely(ssh_state == NULL)) { + if (unlikely(state == NULL)) { return 0; } - if (ssh_state->cli_hdr.software_version == NULL || - ssh_state->srv_hdr.software_version == NULL) - return 0; - json_t *js = CreateJSONHeader(p, LOG_DIR_FLOW, "ssh"); if (unlikely(js == NULL)) return 0; JsonAddCommonOptions(&ssh_ctx->cfg, p, f, js); - json_t *tjs = json_object(); - if (tjs == NULL) { - free(js); - return 0; - } - /* reset */ MemBufferReset(aft->buffer); - JsonSshLogJSON(tjs, ssh_state); - + json_t *tjs = rs_ssh_log_json(txptr); + if (unlikely(tjs == NULL)) { + free(js); + return 0; + } json_object_set_new(js, "ssh", tjs); OutputJSONBuffer(js, ssh_ctx->file_ctx, &aft->buffer); @@ -261,13 +230,13 @@ void JsonSshLogRegister (void) OutputRegisterTxModuleWithProgress(LOGGER_JSON_SSH, "JsonSshLog", "ssh-json-log", OutputSshLogInit, ALPROTO_SSH, JsonSshLogger, - SSH_STATE_BANNER_DONE, SSH_STATE_BANNER_DONE, + SshStateBannerDone, SshStateBannerDone, JsonSshLogThreadInit, JsonSshLogThreadDeinit, NULL); /* also register as child of eve-log */ OutputRegisterTxSubModuleWithProgress(LOGGER_JSON_SSH, "eve-log", "JsonSshLog", "eve-log.ssh", OutputSshLogInitSub, ALPROTO_SSH, JsonSshLogger, - SSH_STATE_BANNER_DONE, SSH_STATE_BANNER_DONE, + SshStateBannerDone, SshStateBannerDone, JsonSshLogThreadInit, JsonSshLogThreadDeinit, NULL); } diff --git a/src/output-json-ssh.h b/src/output-json-ssh.h index aa939b0934..d0f9d3fc7d 100644 --- a/src/output-json-ssh.h +++ b/src/output-json-ssh.h @@ -26,8 +26,4 @@ void JsonSshLogRegister(void); -#include "app-layer-ssh.h" - -void JsonSshLogJSON(json_t *js, SshState *tx); - #endif /* __OUTPUT_JSON_SSH_H__ */ diff --git a/src/output-lua.c b/src/output-lua.c index a7709b635f..283d5f72e0 100644 --- a/src/output-lua.c +++ b/src/output-lua.c @@ -823,8 +823,8 @@ static OutputInitResult OutputLuaLogInit(ConfNode *conf) } else if (opts.alproto == ALPROTO_SSH) { om->TxLogFunc = LuaTxLogger; om->alproto = ALPROTO_SSH; - om->tc_log_progress = SSH_STATE_BANNER_DONE; - om->ts_log_progress = SSH_STATE_BANNER_DONE; + om->tc_log_progress = SshStateBannerDone; + om->ts_log_progress = SshStateBannerDone; AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SSH); } else if (opts.alproto == ALPROTO_SMTP) { om->TxLogFunc = LuaTxLogger; diff --git a/src/util-lua-ssh.c b/src/util-lua-ssh.c index 9370798a8a..4b52ece34c 100644 --- a/src/util-lua-ssh.c +++ b/src/util-lua-ssh.c @@ -47,6 +47,7 @@ #include "util-proto-name.h" #include "util-logopenfile.h" #include "util-time.h" +#include "rust.h" #ifdef HAVE_LUA @@ -63,14 +64,17 @@ static int GetServerProtoVersion(lua_State *luastate, const Flow *f) void *state = FlowGetAppState(f); if (state == NULL) return LuaCallbackError(luastate, "error: no app layer state"); + const uint8_t *protocol = NULL; + uint32_t b_len = 0; - SshState *ssh_state = (SshState *)state; - - if (ssh_state->srv_hdr.proto_version == NULL) + void *tx = rs_ssh_state_get_tx(state, 0); + if (rs_ssh_tx_get_protocol(tx, &protocol, &b_len, STREAM_TOCLIENT) != 1) + return LuaCallbackError(luastate, "error: no server proto version"); + if (protocol == NULL || b_len == 0) { return LuaCallbackError(luastate, "error: no server proto version"); + } - return LuaPushStringBuffer(luastate, ssh_state->srv_hdr.proto_version, - strlen((char *)ssh_state->srv_hdr.proto_version)); + return LuaPushStringBuffer(luastate, protocol, b_len); } static int SshGetServerProtoVersion(lua_State *luastate) @@ -95,13 +99,17 @@ static int GetServerSoftwareVersion(lua_State *luastate, const Flow *f) if (state == NULL) return LuaCallbackError(luastate, "error: no app layer state"); - SshState *ssh_state = (SshState *)state; + const uint8_t *software = NULL; + uint32_t b_len = 0; - if (ssh_state->srv_hdr.software_version == NULL) + void *tx = rs_ssh_state_get_tx(state, 0); + if (rs_ssh_tx_get_software(tx, &software, &b_len, STREAM_TOCLIENT) != 1) + return LuaCallbackError(luastate, "error: no server software version"); + if (software == NULL || b_len == 0) { return LuaCallbackError(luastate, "error: no server software version"); + } - return LuaPushStringBuffer(luastate, ssh_state->srv_hdr.software_version, - strlen((char *)ssh_state->srv_hdr.software_version)); + return LuaPushStringBuffer(luastate, software, b_len); } static int SshGetServerSoftwareVersion(lua_State *luastate) @@ -126,13 +134,17 @@ static int GetClientProtoVersion(lua_State *luastate, const Flow *f) if (state == NULL) return LuaCallbackError(luastate, "error: no app layer state"); - SshState *ssh_state = (SshState *)state; + const uint8_t *protocol = NULL; + uint32_t b_len = 0; - if (ssh_state->cli_hdr.proto_version == NULL) + void *tx = rs_ssh_state_get_tx(state, 0); + if (rs_ssh_tx_get_protocol(tx, &protocol, &b_len, STREAM_TOSERVER) != 1) return LuaCallbackError(luastate, "error: no client proto version"); + if (protocol == NULL || b_len == 0) { + return LuaCallbackError(luastate, "error: no client proto version"); + } - return LuaPushStringBuffer(luastate, ssh_state->cli_hdr.proto_version, - strlen((char *)ssh_state->cli_hdr.proto_version)); + return LuaPushStringBuffer(luastate, protocol, b_len); } static int SshGetClientProtoVersion(lua_State *luastate) @@ -157,13 +169,17 @@ static int GetClientSoftwareVersion(lua_State *luastate, const Flow *f) if (state == NULL) return LuaCallbackError(luastate, "error: no app layer state"); - SshState *ssh_state = (SshState *)state; + const uint8_t *software = NULL; + uint32_t b_len = 0; - if (ssh_state->cli_hdr.software_version == NULL) + void *tx = rs_ssh_state_get_tx(state, 0); + if (rs_ssh_tx_get_software(tx, &software, &b_len, STREAM_TOSERVER) != 1) + return LuaCallbackError(luastate, "error: no client software version"); + if (software == NULL || b_len == 0) { return LuaCallbackError(luastate, "error: no client software version"); + } - return LuaPushStringBuffer(luastate, ssh_state->cli_hdr.software_version, - strlen((char *)ssh_state->cli_hdr.software_version)); + return LuaPushStringBuffer(luastate, software, b_len); } static int SshGetClientSoftwareVersion(lua_State *luastate)