Skip to content

Commit

Permalink
ssh: frames support
Browse files Browse the repository at this point in the history
Ticket: 5734
  • Loading branch information
catenacyber committed Jun 27, 2024
1 parent ce727cf commit 8b0886e
Showing 1 changed file with 69 additions and 10 deletions.
79 changes: 69 additions & 10 deletions rust/src/ssh/ssh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::core::*;
use nom7::Err;
use std::ffi::CString;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::frames::Frame;

static mut ALPROTO_SSH: AppProto = ALPROTO_UNKNOWN;
static HASSH_ENABLED: AtomicBool = AtomicBool::new(false);
Expand All @@ -29,6 +30,14 @@ fn hassh_is_enabled() -> bool {
HASSH_ENABLED.load(Ordering::Relaxed)
}

// app-layer-frame-documentation tag start: FrameType enum
#[derive(AppLayerFrameType)]
pub enum SshFrameType {
RecordHdr,
RecordData,
RecordPdu,
}

#[derive(AppLayerEvent)]
pub enum SSHEvent {
InvalidBanner,
Expand Down Expand Up @@ -109,6 +118,7 @@ impl SSHState {

fn parse_record(
&mut self, mut input: &[u8], resp: bool, pstate: *mut std::os::raw::c_void,
flow: *const Flow, stream_slice: &StreamSlice,
) -> AppLayerResult {
let (hdr, ohdr) = if !resp {
(&mut self.transaction.cli_hdr, &self.transaction.srv_hdr)
Expand Down Expand Up @@ -149,6 +159,30 @@ impl SSHState {
while !input.is_empty() {
match parser::ssh_parse_record(input) {
Ok((rem, head)) => {
let _pdu = Frame::new(
flow,
stream_slice,
input,
SSH_RECORD_HEADER_LEN as i64,
SshFrameType::RecordHdr as u8,
Some(0),
);
let _pdu = Frame::new(
flow,
stream_slice,
&input[SSH_RECORD_HEADER_LEN..],
(head.pkt_len - 2) as i64,
SshFrameType::RecordData as u8,
Some(0),
);
let _pdu = Frame::new(
flow,
stream_slice,
input,
(head.pkt_len + 4) as i64,
SshFrameType::RecordPdu as u8,
Some(0),
);
SCLogDebug!("SSH valid record {}", head);
match head.msg_code {
parser::MessageCode::Kexinit if hassh_is_enabled() => {
Expand Down Expand Up @@ -180,6 +214,30 @@ impl SSHState {
Err(Err::Incomplete(_)) => {
match parser::ssh_parse_record_header(input) {
Ok((rem, head)) => {
let _pdu = Frame::new(
flow,
stream_slice,
input,
SSH_RECORD_HEADER_LEN as i64,
SshFrameType::RecordHdr as u8,
Some(0),
);
let _pdu = Frame::new(
flow,
stream_slice,
&input[SSH_RECORD_HEADER_LEN..],
(head.pkt_len - 2) as i64,
SshFrameType::RecordData as u8,
Some(0),
);
let _pdu = Frame::new(
flow,
stream_slice,
input,
(head.pkt_len + 4) as i64,
SshFrameType::RecordPdu as u8,
Some(0),
);
SCLogDebug!("SSH valid record header {}", head);
let remlen = rem.len() as u32;
hdr.record_left = head.pkt_len - 2 - remlen;
Expand Down Expand Up @@ -239,6 +297,7 @@ impl SSHState {

fn parse_banner(
&mut self, input: &[u8], resp: bool, pstate: *mut std::os::raw::c_void,
flow: *const Flow, stream_slice: &StreamSlice,
) -> AppLayerResult {
let hdr = if !resp {
&mut self.transaction.cli_hdr
Expand All @@ -248,7 +307,7 @@ impl SSHState {
if hdr.flags == SSHConnectionState::SshStateBannerWaitEol {
match parser::ssh_parse_line(input) {
Ok((rem, _)) => {
let mut r = self.parse_record(rem, resp, pstate);
let mut r = self.parse_record(rem, resp, pstate, flow, stream_slice);
if r.is_incomplete() {
//adds bytes consumed by banner to incomplete result
r.consumed += (input.len() - rem.len()) as u32;
Expand Down Expand Up @@ -288,7 +347,7 @@ impl SSHState {
);
self.set_event(SSHEvent::LongBanner);
}
let mut r = self.parse_record(rem, resp, pstate);
let mut r = self.parse_record(rem, resp, pstate, flow, stream_slice);
if r.is_incomplete() {
//adds bytes consumed by banner to incomplete result
r.consumed += (input.len() - rem.len()) as u32;
Expand Down Expand Up @@ -352,33 +411,33 @@ pub extern "C" fn rs_ssh_state_tx_free(_state: *mut std::os::raw::c_void, _tx_id

#[no_mangle]
pub unsafe extern "C" fn rs_ssh_parse_request(
_flow: *const Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void,
flow: *const Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void,
stream_slice: StreamSlice,
_data: *const std::os::raw::c_void
) -> AppLayerResult {
let state = &mut cast_pointer!(state, SSHState);
let buf = stream_slice.as_slice();
let hdr = &mut state.transaction.cli_hdr;
if hdr.flags < SSHConnectionState::SshStateBannerDone {
return state.parse_banner(buf, false, pstate);
return state.parse_banner(buf, false, pstate, flow, &stream_slice);
} else {
return state.parse_record(buf, false, pstate);
return state.parse_record(buf, false, pstate, flow, &stream_slice);
}
}

#[no_mangle]
pub unsafe extern "C" fn rs_ssh_parse_response(
_flow: *const Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void,
flow: *const Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void,
stream_slice: StreamSlice,
_data: *const std::os::raw::c_void
) -> AppLayerResult {
let state = &mut cast_pointer!(state, SSHState);
let buf = stream_slice.as_slice();
let hdr = &mut state.transaction.srv_hdr;
if hdr.flags < SSHConnectionState::SshStateBannerDone {
return state.parse_banner(buf, true, pstate);
return state.parse_banner(buf, true, pstate, flow, &stream_slice);
} else {
return state.parse_record(buf, true, pstate);
return state.parse_record(buf, true, pstate, flow, &stream_slice);
}
}

Expand Down Expand Up @@ -465,8 +524,8 @@ pub unsafe extern "C" fn rs_ssh_register_parser() {
apply_tx_config: None,
flags: 0,
truncate: None,
get_frame_id_by_name: None,
get_frame_name_by_id: None,
get_frame_id_by_name: Some(SshFrameType::ffi_id_from_name),
get_frame_name_by_id: Some(SshFrameType::ffi_name_from_id),
};

let ip_proto_str = CString::new("tcp").unwrap();
Expand Down

0 comments on commit 8b0886e

Please sign in to comment.