From 3f4dad8676d9c4d80c0b0242c0d3ded308ed1315 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Fri, 20 Jan 2023 16:12:54 -0600 Subject: [PATCH] ftp: add events for command too long Issue: 5235 --- rules/Makefile.am | 1 + rules/ftp-events.rules | 6 +++++ rust/cbindgen.toml | 3 ++- rust/src/ftp/event.rs | 50 ++++++++++++++++++++++++++++++++++++++++++ rust/src/ftp/mod.rs | 2 ++ src/app-layer-ftp.c | 15 +++++++++++++ 6 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 rules/ftp-events.rules create mode 100644 rust/src/ftp/event.rs diff --git a/rules/Makefile.am b/rules/Makefile.am index 0eaf5be890..4c0c744c9b 100644 --- a/rules/Makefile.am +++ b/rules/Makefile.am @@ -7,6 +7,7 @@ dhcp-events.rules \ dnp3-events.rules \ dns-events.rules \ files.rules \ +ftp-events.rules \ http-events.rules \ http2-events.rules \ ipsec-events.rules \ diff --git a/rules/ftp-events.rules b/rules/ftp-events.rules new file mode 100644 index 0000000000..d32c93f327 --- /dev/null +++ b/rules/ftp-events.rules @@ -0,0 +1,6 @@ +# FTP app-layer event rules +# +# SID range start: 2232000 + +alert ftp any any -> any any (msg:"SURICATA FTP Request command too long"; flow:to_server; app-layer-event:ftp.request_command_too_long; classtype:protocol-command-decode; sid:2232000; rev:1;) +alert ftp any any -> any any (msg:"SURICATA FTP Response command too long"; flow:to_client; app-layer-event:ftp.response_command_too_long; classtype:protocol-command-decode; sid:2232001; rev:1;) diff --git a/rust/cbindgen.toml b/rust/cbindgen.toml index 635b972945..15c56e2e7d 100644 --- a/rust/cbindgen.toml +++ b/rust/cbindgen.toml @@ -80,7 +80,8 @@ include = [ "ModbusState", "CMark", "QuicState", - "QuicTransaction" + "QuicTransaction", + "FtpEvent", ] # A list of items to not include in the generated bindings diff --git a/rust/src/ftp/event.rs b/rust/src/ftp/event.rs new file mode 100644 index 0000000000..04cc9e3f1e --- /dev/null +++ b/rust/src/ftp/event.rs @@ -0,0 +1,50 @@ +/* Copyright (C) 2023 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 crate::core::AppLayerEventType; +use std::os::raw::{c_char, c_int}; + +#[derive(Debug, PartialEq, Eq, AppLayerEvent)] +#[repr(C)] +pub enum FtpEvent { + #[name("request_command_too_long")] + FtpEventRequestCommandTooLong, + #[name("response_command_too_long")] + FtpEventResponseCommandTooLong, +} + +/// Wrapper around the Rust generic function for get_event_info. +/// +/// # Safety +/// Unsafe as called from C. +#[no_mangle] +pub unsafe extern "C" fn ftp_get_event_info( + event_name: *const c_char, event_id: *mut c_int, event_type: *mut AppLayerEventType, +) -> c_int { + crate::applayer::get_event_info::(event_name, event_id, event_type) +} + +/// Wrapper around the Rust generic function for get_event_info_by_id. +/// +/// # Safety +/// Unsafe as called from C. +#[no_mangle] +pub unsafe extern "C" fn ftp_get_event_info_by_id( + event_id: c_int, event_name: *mut *const c_char, event_type: *mut AppLayerEventType, +) -> c_int { + crate::applayer::get_event_info_by_id::(event_id, event_name, event_type) as c_int +} diff --git a/rust/src/ftp/mod.rs b/rust/src/ftp/mod.rs index 4c01b78dc1..1a60ca470c 100644 --- a/rust/src/ftp/mod.rs +++ b/rust/src/ftp/mod.rs @@ -24,6 +24,8 @@ use std; use std::str; use std::str::FromStr; +pub mod event; + // We transform an integer string into a i64, ignoring surrounding whitespaces // We look for a digit suite, and try to convert it. // If either str::from_utf8 or FromStr::from_str fail, diff --git a/src/app-layer-ftp.c b/src/app-layer-ftp.c index 2857e02b55..4f7ed0b16b 100644 --- a/src/app-layer-ftp.c +++ b/src/app-layer-ftp.c @@ -323,6 +323,10 @@ static void FTPTransactionFree(FTPTransaction *tx) FTPStringFree(str); } + if (tx->tx_data.events) { + AppLayerDecoderEventsFreeEvents(&tx->tx_data.events); + } + FTPFree(tx, sizeof(*tx)); } @@ -526,6 +530,10 @@ static AppLayerResult FTPParseRequest(Flow *f, void *ftp_state, AppLayerParserSt tx->request_length = CopyCommandLine(&tx->request, &line); tx->request_truncated = state->current_line_truncated; + if (tx->request_truncated) { + AppLayerDecoderEventsSetEventRaw(&tx->tx_data.events, FtpEventRequestCommandTooLong); + } + /* change direction (default to server) so expectation will handle * the correct message when expectation will match. * For ftp active mode, data connection direction is opposite to @@ -761,6 +769,10 @@ static AppLayerResult FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserS if (likely(response)) { response->len = CopyCommandLine(&response->str, &line); response->truncated = state->current_line_truncated; + if (response->truncated) { + AppLayerDecoderEventsSetEventRaw( + &tx->tx_data.events, FtpEventResponseCommandTooLong); + } TAILQ_INSERT_TAIL(&tx->response_list, response, next); } } @@ -1334,6 +1346,9 @@ void RegisterFTPParsers(void) AppLayerParserRegisterStateProgressCompletionStatus( ALPROTO_FTPDATA, FTPDATA_STATE_FINISHED, FTPDATA_STATE_FINISHED); + AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_FTP, ftp_get_event_info); + AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_FTP, ftp_get_event_info_by_id); + sbcfg.buf_size = 4096; sbcfg.Calloc = FTPCalloc; sbcfg.Realloc = FTPRealloc;