diff --git a/etc/schema.json b/etc/schema.json index f355ecb03e..65d3a3793e 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -2842,6 +2842,11 @@ "description": "Array of IMAP request lines in this transaction", "items": { "type": "string" + }, + "suricata": { + "keywords": [ + "imap.request" + ] } }, "responses": { @@ -2849,6 +2854,11 @@ "description": "Array of IMAP response lines in this transaction", "items": { "type": "string" + }, + "suricata": { + "keywords": [ + "imap.response" + ] } } } diff --git a/rust/src/imap/detect.rs b/rust/src/imap/detect.rs new file mode 100644 index 0000000000..6c1e6f93eb --- /dev/null +++ b/rust/src/imap/detect.rs @@ -0,0 +1,153 @@ +/* Copyright (C) 2026 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. + */ + +// Author: Giuseppe Longo + +use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER}; +use crate::detect::{ + SIGMATCH_INFO_MULTI_BUFFER, SIGMATCH_INFO_STICKY_BUFFER, SIGMATCH_NOOPT, + SIGMATCH_SUPPORT_FIREWALL, +}; +use crate::imap::imap::{ImapTransaction, ALPROTO_IMAP}; +use std::ffi::CString; +use std::os::raw::{c_int, c_void}; +use suricata_sys::sys::{ + DetectEngineCtx, DetectEngineThreadCtx, SCDetectBufferSetActiveList, + SCDetectHelperKeywordRegister, SCDetectHelperKeywordSetCleanCString, + SCDetectHelperMultiBufferProgressMpmRegister, SCDetectSignatureSetAppProto, + SCSigTableAppLiteElmt, Signature, +}; + +static mut G_IMAP_REQUEST_BUFFER_ID: c_int = 0; +static mut G_IMAP_RESPONSE_BUFFER_ID: c_int = 0; + +unsafe extern "C" fn imap_detect_request_setup( + de: *mut DetectEngineCtx, s: *mut Signature, _raw: *const std::os::raw::c_char, +) -> c_int { + if SCDetectSignatureSetAppProto(s, ALPROTO_IMAP) != 0 { + return -1; + } + if SCDetectBufferSetActiveList(de, s, G_IMAP_REQUEST_BUFFER_ID) < 0 { + return -1; + } + 0 +} + +unsafe extern "C" fn imap_tx_get_request( + _de: *mut DetectEngineThreadCtx, tx: *const c_void, _flags: u8, local_id: u32, + buffer: *mut *const u8, buffer_len: *mut u32, +) -> bool { + let tx = cast_pointer!(tx, ImapTransaction); + + let Some(request) = tx.requests.get(local_id as usize) else { + return false; + }; + *buffer = request.raw_line.as_ptr(); + *buffer_len = request.raw_line.len() as u32; + true +} + +unsafe extern "C" fn imap_detect_response_setup( + de: *mut DetectEngineCtx, s: *mut Signature, _raw: *const std::os::raw::c_char, +) -> c_int { + if SCDetectSignatureSetAppProto(s, ALPROTO_IMAP) != 0 { + return -1; + } + if SCDetectBufferSetActiveList(de, s, G_IMAP_RESPONSE_BUFFER_ID) < 0 { + return -1; + } + 0 +} + +unsafe extern "C" fn imap_tx_get_response( + _de: *mut DetectEngineThreadCtx, tx: *const c_void, _flags: u8, local_id: u32, + buffer: *mut *const u8, buffer_len: *mut u32, +) -> bool { + let tx = cast_pointer!(tx, ImapTransaction); + + let Some(line) = tx.response_lines.get(local_id as usize) else { + return false; + }; + *buffer = line.as_ptr(); + *buffer_len = line.len() as u32; + true +} + +#[no_mangle] +pub unsafe extern "C" fn SCDetectImapRegister() { + let name = CString::new("imap.request").unwrap().into_raw(); + let desc = CString::new("match on IMAP request line") + .unwrap() + .into_raw(); + let url = CString::new("/rules/imap-keywords.html#imap-request") + .unwrap() + .into_raw(); + let kw = SCSigTableAppLiteElmt { + name, + desc, + url, + Setup: Some(imap_detect_request_setup), + flags: SIGMATCH_NOOPT + | SIGMATCH_INFO_STICKY_BUFFER + | SIGMATCH_INFO_MULTI_BUFFER + | SIGMATCH_SUPPORT_FIREWALL, + AppLayerTxMatch: None, + Free: None, + }; + let r = SCDetectHelperKeywordRegister(&kw); + SCDetectHelperKeywordSetCleanCString(r); + + G_IMAP_REQUEST_BUFFER_ID = SCDetectHelperMultiBufferProgressMpmRegister( + b"imap.request\0".as_ptr() as *const libc::c_char, + b"IMAP REQUEST LINE\0".as_ptr() as *const libc::c_char, + ALPROTO_IMAP, + STREAM_TOSERVER, + Some(imap_tx_get_request), + 0, + ); + + let name = CString::new("imap.response").unwrap().into_raw(); + let desc = CString::new("match on IMAP response line") + .unwrap() + .into_raw(); + let url = CString::new("/rules/imap-keywords.html#imap-response") + .unwrap() + .into_raw(); + let kw = SCSigTableAppLiteElmt { + name, + desc, + url, + Setup: Some(imap_detect_response_setup), + flags: SIGMATCH_NOOPT + | SIGMATCH_INFO_STICKY_BUFFER + | SIGMATCH_INFO_MULTI_BUFFER + | SIGMATCH_SUPPORT_FIREWALL, + AppLayerTxMatch: None, + Free: None, + }; + let r = SCDetectHelperKeywordRegister(&kw); + SCDetectHelperKeywordSetCleanCString(r); + + G_IMAP_RESPONSE_BUFFER_ID = SCDetectHelperMultiBufferProgressMpmRegister( + b"imap.response\0".as_ptr() as *const libc::c_char, + b"IMAP RESPONSE LINE\0".as_ptr() as *const libc::c_char, + ALPROTO_IMAP, + STREAM_TOCLIENT, + Some(imap_tx_get_response), + 1, + ); +} diff --git a/rust/src/imap/mod.rs b/rust/src/imap/mod.rs index 739931da27..612a2aa13a 100644 --- a/rust/src/imap/mod.rs +++ b/rust/src/imap/mod.rs @@ -17,6 +17,7 @@ // Author: Giuseppe Longo +pub mod detect; pub mod imap; pub mod logger; pub mod parser; diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 0c94152a10..c9d5f73e90 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -861,6 +861,7 @@ void SigTableSetup(void) SCDetectSipRegister(); SCDetectTemplateRegister(); SCDetectLdapRegister(); + SCDetectImapRegister(); SCDetectSdpRegister(); SCDetectDNSRegister(); SCDetectPgsqlRegister();