diff --git a/doc/userguide/rules/enip-keyword.rst b/doc/userguide/rules/enip-keyword.rst index 9d834334b4..651a224bb7 100644 --- a/doc/userguide/rules/enip-keyword.rst +++ b/doc/userguide/rules/enip-keyword.rst @@ -86,6 +86,8 @@ It uses a 32-bit unsigned integer as value. enip.cip_instance uses an :ref:`unsigned 32-bits integer `. +enip.cip_instance is also a :ref:`multi-integer `. + Examples:: enip.cip_instance:1; @@ -99,6 +101,8 @@ It uses a 32-bit unsigned integer as value. enip.cip_class uses an :ref:`unsigned 32-bits integer `. +enip.cip_class is also a :ref:`multi-integer `. + This allows to match without needing to match on cip.service. Examples:: diff --git a/rust/src/enip/detect.rs b/rust/src/enip/detect.rs index 8d106c2da7..6899479df9 100644 --- a/rust/src/enip/detect.rs +++ b/rust/src/enip/detect.rs @@ -26,8 +26,8 @@ use std::os::raw::{c_int, c_void}; use super::constant::{EnipCommand, EnipStatus}; use super::enip::{EnipTransaction, ALPROTO_ENIP}; use super::parser::{ - CipData, CipDir, EnipCipRequestPayload, EnipCipResponsePayload, EnipItemPayload, EnipPayload, - CIP_MULTIPLE_SERVICE, + CipData, CipDir, EnipCipPathSegment, EnipCipRequestPayload, EnipCipResponsePayload, + EnipItemPayload, EnipPayload, CIP_MULTIPLE_SERVICE, }; use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER}; @@ -294,18 +294,34 @@ fn enip_get_status(tx: &EnipTransaction, direction: Direction) -> Option { return None; } +macro_rules! get_enip_segment_val { + ($segment_type:expr) => { + |seg: &EnipCipPathSegment| { + if seg.segment_type >> 2 == $segment_type { + Some(seg.value) + } else { + None + } + } + }; +} + fn enip_cip_match_segment( - d: &CipData, ctx: &DetectUintData, segment_type: u8, + d: &CipData, ctx: &DetectUintArrayData, segment_type: u8, done: bool, ) -> std::os::raw::c_int { if let CipDir::Request(req) = &d.cipdir { - for seg in req.path.iter() { - if seg.segment_type >> 2 == segment_type && detect_match_uint(ctx, seg.value) { - return 1; - } + let r = detect_uint_match_at_index::( + &req.path, + ctx, + get_enip_segment_val!(segment_type), + done, + ); + if r == 1 { + return 1; } if let EnipCipRequestPayload::Multiple(m) = &req.payload { for p in m.packet_list.iter() { - if enip_cip_match_segment(p, ctx, segment_type) == 1 { + if enip_cip_match_segment(p, ctx, segment_type, done) == 1 { return 1; } } @@ -315,13 +331,13 @@ fn enip_cip_match_segment( } fn enip_tx_has_cip_segment( - tx: &EnipTransaction, ctx: &DetectUintData, segment_type: u8, + tx: &EnipTransaction, ctx: &DetectUintArrayData, segment_type: u8, ) -> std::os::raw::c_int { if let Some(pdu) = &tx.request { if let EnipPayload::Cip(c) = &pdu.payload { for item in c.items.iter() { if let EnipItemPayload::Data(d) = &item.payload { - return enip_cip_match_segment(&d.cip, ctx, segment_type); + return enip_cip_match_segment(&d.cip, ctx, segment_type, tx.done); } } } @@ -329,7 +345,9 @@ fn enip_tx_has_cip_segment( return 0; } -fn enip_cip_match_attribute(d: &CipData, ctx: &DetectUintArrayData, done: bool) -> std::os::raw::c_int { +fn enip_cip_match_attribute( + d: &CipData, ctx: &DetectUintArrayData, done: bool, +) -> std::os::raw::c_int { if let CipDir::Request(req) = &d.cipdir { let mut vals = Vec::new(); for seg in req.path.iter() { @@ -609,7 +627,7 @@ unsafe extern "C" fn cip_class_setup( if SCDetectSignatureSetAppProto(s, ALPROTO_ENIP) != 0 { return -1; } - let ctx = SCDetectU32Parse(raw) as *mut c_void; + let ctx = SCDetectU32ArrayParse(raw) as *mut c_void; if ctx.is_null() { return -1; } @@ -633,14 +651,14 @@ unsafe extern "C" fn cip_class_match( tx: *mut c_void, _sig: *const Signature, ctx: *const SigMatchCtx, ) -> c_int { let tx = cast_pointer!(tx, EnipTransaction); - let ctx = cast_pointer!(ctx, DetectUintData); + let ctx = cast_pointer!(ctx, DetectUintArrayData); return enip_tx_has_cip_segment(tx, ctx, 8); } unsafe extern "C" fn cip_class_free(_de: *mut DetectEngineCtx, ctx: *mut c_void) { // Just unbox... - let ctx = cast_pointer!(ctx, DetectUintData); - SCDetectU32Free(ctx); + let ctx = cast_pointer!(ctx, DetectUintArrayData); + SCDetectU32ArrayFree(ctx); } unsafe extern "C" fn vendor_id_setup( @@ -1222,7 +1240,7 @@ unsafe extern "C" fn cip_instance_setup( if SCDetectSignatureSetAppProto(s, ALPROTO_ENIP) != 0 { return -1; } - let ctx = SCDetectU32Parse(raw) as *mut c_void; + let ctx = SCDetectU32ArrayParse(raw) as *mut c_void; if ctx.is_null() { return -1; } @@ -1246,14 +1264,14 @@ unsafe extern "C" fn cip_instance_match( tx: *mut c_void, _sig: *const Signature, ctx: *const SigMatchCtx, ) -> c_int { let tx = cast_pointer!(tx, EnipTransaction); - let ctx = cast_pointer!(ctx, DetectUintData); + let ctx = cast_pointer!(ctx, DetectUintArrayData); return enip_tx_has_cip_segment(tx, ctx, 9); } unsafe extern "C" fn cip_instance_free(_de: *mut DetectEngineCtx, ctx: *mut c_void) { // Just unbox... - let ctx = cast_pointer!(ctx, DetectUintData); - SCDetectU32Free(ctx); + let ctx = cast_pointer!(ctx, DetectUintArrayData); + SCDetectU32ArrayFree(ctx); } unsafe extern "C" fn cip_extendedstatus_setup(