ssh: limit length for banner logs

Ticket: 6770
(cherry picked from commit c4b8fb7aca)
pull/10669/head
Philippe Antoine 1 year ago committed by Victor Julien
parent a947228259
commit b9963b3e29

@ -17,6 +17,7 @@
#![allow(clippy::missing_safety_doc)]
use std::collections::TryReserveError;
use std::ffi::CStr;
use std::os::raw::c_char;
use std::str::Utf8Error;
@ -27,6 +28,7 @@ const INIT_SIZE: usize = 4096;
pub enum JsonError {
InvalidState,
Utf8Error(Utf8Error),
Memory,
}
impl std::error::Error for JsonError {}
@ -36,10 +38,17 @@ impl std::fmt::Display for JsonError {
match self {
JsonError::InvalidState => write!(f, "invalid state"),
JsonError::Utf8Error(ref e) => e.fmt(f),
JsonError::Memory => write!(f, "memory error"),
}
}
}
impl From<TryReserveError> for JsonError {
fn from(_: TryReserveError) -> Self {
JsonError::Memory
}
}
impl From<Utf8Error> for JsonError {
fn from(e: Utf8Error) -> Self {
JsonError::Utf8Error(e)
@ -427,6 +436,22 @@ impl JsonBuilder {
}
}
/// Set a key and a string value (from bytes) on an object, with a limited size
pub fn set_string_from_bytes_limited(&mut self, key: &str, val: &[u8], limit: usize) -> Result<&mut Self, JsonError> {
let mut valtrunc = Vec::new();
let val = if val.len() > limit {
valtrunc.extend_from_slice(&val[..limit]);
valtrunc.extend_from_slice(b"[truncated]");
&valtrunc
} else {
val
};
match std::str::from_utf8(val) {
Ok(s) => self.set_string(key, s),
Err(_) => self.set_string(key, &try_string_from_bytes(val)?),
}
}
/// Set a key and an unsigned integer type on an object.
pub fn set_uint(&mut self, key: &str, val: u64) -> Result<&mut Self, JsonError> {
match self.current_state() {
@ -570,6 +595,26 @@ fn string_from_bytes(input: &[u8]) -> String {
return out;
}
/// A Suricata specific function to create a string from bytes when UTF-8 decoding fails.
///
/// For bytes over 0x0f, we encode as hex like "\xf2".
fn try_string_from_bytes(input: &[u8]) -> Result<String, JsonError> {
let mut out = String::new();
// Allocate enough data to handle the worst case scenario of every
// byte needing to be presented as a byte.
out.try_reserve(input.len() * 4)?;
for b in input.iter() {
if *b < 128 {
out.push(*b as char);
} else {
out.push_str(&format!("\\x{:02x}", *b));
}
}
return Ok(out);
}
#[no_mangle]
pub extern "C" fn jb_new_object() -> *mut JsonBuilder {
let boxed = Box::new(JsonBuilder::new_object());

@ -15,7 +15,7 @@
* 02110-1301, USA.
*/
use super::ssh::SSHTransaction;
use super::ssh::{SSHTransaction, SSH_MAX_BANNER_LEN};
use crate::jsonbuilder::{JsonBuilder, JsonError};
fn log_ssh(tx: &SSHTransaction, js: &mut JsonBuilder) -> Result<bool, JsonError> {
@ -24,9 +24,9 @@ fn log_ssh(tx: &SSHTransaction, js: &mut JsonBuilder) -> Result<bool, JsonError>
}
if tx.cli_hdr.protover.len() > 0 {
js.open_object("client")?;
js.set_string_from_bytes("proto_version", &tx.cli_hdr.protover)?;
if tx.cli_hdr.swver.len() > 0 {
js.set_string_from_bytes("software_version", &tx.cli_hdr.swver)?;
js.set_string_from_bytes_limited("proto_version", &tx.cli_hdr.protover, SSH_MAX_BANNER_LEN)?;
if !tx.cli_hdr.swver.is_empty() {
js.set_string_from_bytes_limited("software_version", &tx.cli_hdr.swver, SSH_MAX_BANNER_LEN)?;
}
if tx.cli_hdr.hassh.len() > 0 || tx.cli_hdr.hassh_string.len() > 0 {
js.open_object("hassh")?;
@ -42,9 +42,9 @@ fn log_ssh(tx: &SSHTransaction, js: &mut JsonBuilder) -> Result<bool, JsonError>
}
if tx.srv_hdr.protover.len() > 0 {
js.open_object("server")?;
js.set_string_from_bytes("proto_version", &tx.srv_hdr.protover)?;
if tx.srv_hdr.swver.len() > 0 {
js.set_string_from_bytes("software_version", &tx.srv_hdr.swver)?;
js.set_string_from_bytes_limited("proto_version", &tx.srv_hdr.protover, SSH_MAX_BANNER_LEN)?;
if !tx.srv_hdr.swver.is_empty() {
js.set_string_from_bytes_limited("software_version", &tx.srv_hdr.swver, SSH_MAX_BANNER_LEN)?;
}
if tx.srv_hdr.hassh.len() > 0 || tx.srv_hdr.hassh_string.len() > 0 {
js.open_object("hassh")?;

@ -59,7 +59,7 @@ pub enum SSHConnectionState {
SshStateFinished = 3,
}
const SSH_MAX_BANNER_LEN: usize = 256;
pub const SSH_MAX_BANNER_LEN: usize = 256;
const SSH_RECORD_HEADER_LEN: usize = 6;
const SSH_MAX_REASSEMBLED_RECORD_LEN: usize = 65535;

Loading…
Cancel
Save