Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this right Victor ?
Setting the frame length that gets over the available data so far...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if final length is unknown, it should set -1 and update the frame's len when the final length is available

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final length is known but we do not have the full data yet. You just told me it is ok

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup

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
8 changes: 7 additions & 1 deletion src/detect.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,13 @@ static void DetectRun(ThreadVars *th_v,
goto end;
}
const TcpSession *ssn = p->flow->protoctx;
if (ssn && (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0) {
bool setting_nopayload = p->flow->alparser &&
AppLayerParserStateIssetFlag(
p->flow->alparser, APP_LAYER_PARSER_NO_INSPECTION) &&
!(p->flags & PKT_NOPAYLOAD_INSPECTION);
// we may be right after disabling app-layer (ssh)
if (ssn &&
((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0 || setting_nopayload)) {
// PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
// PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
Expand Down