From: Simon Dugas Date: Fri, 19 Feb 2021 21:31:33 +0000 (-0500) Subject: modbus: move tests from c to rust X-Git-Tag: suricata-7.0.0-beta1~1641 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8342641477466e11727e31bb1efcd28bd2c55da7;p=thirdparty%2Fsuricata.git modbus: move tests from c to rust Move tests in a seperate commit so that we can use the previous one for regression testing. This also gets rid of the temporary glue that made the C tests work with the rust implementation. --- diff --git a/rust/src/modbus/detect.rs b/rust/src/modbus/detect.rs index af402f1828..68aa4a6642 100644 --- a/rust/src/modbus/detect.rs +++ b/rust/src/modbus/detect.rs @@ -56,73 +56,6 @@ pub struct DetectModbusRust { value: Option>, } -/// TODO: remove these after regression testing commit -#[no_mangle] -pub extern "C" fn rs_modbus_get_category(modbus: *const DetectModbusRust) -> u8 { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.category.map(|val| val.bits()).unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn rs_modbus_get_function(modbus: *const DetectModbusRust) -> u8 { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.function.map(|val| val as u8).unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn rs_modbus_get_subfunction(modbus: *const DetectModbusRust) -> u16 { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.subfunction.unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn rs_modbus_get_has_subfunction(modbus: *const DetectModbusRust) -> bool { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.subfunction.is_some() -} - -#[no_mangle] -pub extern "C" fn rs_modbus_get_access_type(modbus: *const DetectModbusRust) -> u8 { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.access_type.map(|val| val.bits()).unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn rs_modbus_get_unit_id_min(modbus: *const DetectModbusRust) -> u16 { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.unit_id.as_ref().map(|val| val.start).unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn rs_modbus_get_unit_id_max(modbus: *const DetectModbusRust) -> u16 { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.unit_id.as_ref().map(|val| val.end).unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn rs_modbus_get_address_min(modbus: *const DetectModbusRust) -> u16 { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.address.as_ref().map(|val| val.start).unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn rs_modbus_get_address_max(modbus: *const DetectModbusRust) -> u16 { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.address.as_ref().map(|val| val.end).unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn rs_modbus_get_data_min(modbus: *const DetectModbusRust) -> u16 { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.value.as_ref().map(|val| val.start).unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn rs_modbus_get_data_max(modbus: *const DetectModbusRust) -> u16 { - let modbus = unsafe { modbus.as_ref() }.unwrap(); - modbus.value.as_ref().map(|val| val.end).unwrap_or(0) -} - impl Default for DetectModbusRust { fn default() -> Self { DetectModbusRust { @@ -537,3 +470,996 @@ fn parse_unit_id(unit_str: &str) -> Result { Ok(modbus) } + +#[cfg(test)] +mod test { + use super::super::modbus::ModbusState; + use super::*; + use crate::applayer::*; + use sawp::parser::Direction; + + #[test] + fn test_parse() { + assert_eq!( + parse_function("function 1"), + Ok(DetectModbusRust { + function: Some(FunctionCode::RdCoils), + ..Default::default() + }) + ); + assert_eq!( + parse_function("function 8, subfunction 4"), + Ok(DetectModbusRust { + function: Some(FunctionCode::Diagnostic), + subfunction: Some(4), + ..Default::default() + }) + ); + assert_eq!( + parse_function("function reserved"), + Ok(DetectModbusRust { + category: Some(Flags::from(CodeCategory::RESERVED)), + ..Default::default() + }) + ); + assert_eq!( + parse_function("function !assigned"), + Ok(DetectModbusRust { + category: Some(!CodeCategory::PUBLIC_ASSIGNED), + ..Default::default() + }) + ); + + assert_eq!( + parse_access("access read"), + Ok(DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + ..Default::default() + }) + ); + assert_eq!( + parse_access("access read discretes"), + Ok(DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::DISCRETES)), + ..Default::default() + }) + ); + assert_eq!( + parse_access("access read, address 1000"), + Ok(DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + address: Some(1000..1000), + ..Default::default() + }) + ); + assert_eq!( + parse_access("access write coils, address <500"), + Ok(DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::COILS)), + address: Some(std::u16::MIN..500), + ..Default::default() + }) + ); + assert_eq!( + parse_access("access write coils, address >500"), + Ok(DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::COILS)), + address: Some(500..std::u16::MAX), + ..Default::default() + }) + ); + assert_eq!( + parse_access("access write holding, address 100, value <1000"), + Ok(DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(100..100), + value: Some(std::u16::MIN..1000), + ..Default::default() + }) + ); + assert_eq!( + parse_access("access write holding, address 100, value 500<>1000"), + Ok(DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(100..100), + value: Some(500..1000), + ..Default::default() + }) + ); + + assert_eq!( + parse_unit_id("unit 10"), + Ok(DetectModbusRust { + unit_id: Some(10..10), + ..Default::default() + }) + ); + assert_eq!( + parse_unit_id("unit 10, function 8, subfunction 4"), + Ok(DetectModbusRust { + function: Some(FunctionCode::Diagnostic), + subfunction: Some(4), + unit_id: Some(10..10), + ..Default::default() + }) + ); + assert_eq!( + parse_unit_id("unit 10, access read, address 1000"), + Ok(DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + unit_id: Some(10..10), + address: Some(1000..1000), + ..Default::default() + }) + ); + assert_eq!( + parse_unit_id("unit <11"), + Ok(DetectModbusRust { + unit_id: Some(std::u16::MIN..11), + ..Default::default() + }) + ); + assert_eq!( + parse_unit_id("unit 10<>500"), + Ok(DetectModbusRust { + unit_id: Some(10..500), + ..Default::default() + }) + ); + + assert_eq!(parse_access("access write holdin"), Err(())); + assert_eq!(parse_access("unt 10"), Err(())); + assert_eq!( + parse_access("access write holding, address 100, value 500<>"), + Err(()) + ); + } + + #[test] + fn test_match() { + let mut modbus = ModbusState::new(); + + // Read/Write Multiple Registers Request + assert_eq!( + modbus.parse( + &[ + 0x12, 0x34, // Transaction ID + 0x00, 0x00, // Protocol ID + 0x00, 0x11, // Length + 0x0a, // Unit ID + 0x17, // Function code + 0x00, 0x03, // Read Starting Address + 0x00, 0x06, // Quantity to Read + 0x00, 0x0E, // Write Starting Address + 0x00, 0x03, // Quantity to Write + 0x06, // Write Byte count + 0x12, 0x34, // Write Registers Value + 0x56, 0x78, 0x9A, 0xBC + ], + Direction::ToServer + ), + AppLayerResult::ok() + ); + assert_eq!(modbus.transactions.len(), 1); + // function 23 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + function: Some(FunctionCode::RdWrMultRegs), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 15, value <4660 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(15..15), + value: Some(std::u16::MIN..4660), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 15, value 4661 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(15..15), + value: Some(4661..4661), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 16, value 20000<>22136 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(16..16), + value: Some(20000..22136), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 16, value 22136<>30000 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(16..16), + value: Some(22136..30000), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 15, value >4660 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(15..15), + value: Some(4660..std::u16::MAX), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 16, value <22137 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(16..16), + value: Some(std::u16::MIN..22137), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 16, value <22137 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(16..16), + value: Some(std::u16::MIN..22137), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 17, value 39612 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(17..17), + value: Some(39612..39612), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 17, value 30000<>39613 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(17..17), + value: Some(30000..39613), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 15, value 4659<>5000 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(15..15), + value: Some(4659..5000), + ..Default::default() + } + ), + 1 + ); + // access write holding, address 17, value >39611 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE | AccessType::HOLDING)), + address: Some(17..17), + value: Some(39611..std::u16::MAX), + ..Default::default() + } + ), + 1 + ); + // unit 12 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + unit_id: Some(12..12), + ..Default::default() + } + ), + 1 + ); + // unit 5<>9 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + unit_id: Some(5..9), + ..Default::default() + } + ), + 1 + ); + // unit 11<>15 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + unit_id: Some(11..15), + ..Default::default() + } + ), + 1 + ); + // unit >11 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + unit_id: Some(11..std::u16::MAX), + ..Default::default() + } + ), + 1 + ); + // unit <9 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + unit_id: Some(std::u16::MIN..9), + ..Default::default() + } + ), + 1 + ); + // unit 10 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + unit_id: Some(10..10), + ..Default::default() + } + ), + 1 + ); + // unit 5<>15 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + unit_id: Some(5..15), + ..Default::default() + } + ), + 1 + ); + // unit >9 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + unit_id: Some(9..std::u16::MAX), + ..Default::default() + } + ), + 1 + ); + // unit <11 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + unit_id: Some(std::u16::MIN..11), + ..Default::default() + } + ), + 1 + ); + // unit 10, function 20 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + function: Some(FunctionCode::RdFileRec), + unit_id: Some(10..10), + ..Default::default() + } + ), + 1 + ); + // unit 11, function 20 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + function: Some(FunctionCode::RdFileRec), + unit_id: Some(11..11), + ..Default::default() + } + ), + 1 + ); + // unit 11, function 23 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + function: Some(FunctionCode::RdWrMultRegs), + unit_id: Some(11..11), + ..Default::default() + } + ), + 1 + ); + // unit 11, function public + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + category: Some(Flags::from( + CodeCategory::PUBLIC_ASSIGNED | CodeCategory::PUBLIC_UNASSIGNED + )), + unit_id: Some(11..11), + ..Default::default() + } + ), + 1 + ); + // unit 10, function user + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + category: Some(Flags::from(CodeCategory::USER_DEFINED)), + unit_id: Some(10..10), + ..Default::default() + } + ), + 1 + ); + // unit 10, function 23 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + function: Some(FunctionCode::RdWrMultRegs), + unit_id: Some(10..10), + ..Default::default() + } + ), + 1 + ); + // unit 10, function public + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + category: Some(Flags::from( + CodeCategory::PUBLIC_ASSIGNED | CodeCategory::PUBLIC_UNASSIGNED + )), + unit_id: Some(10..10), + ..Default::default() + } + ), + 1 + ); + // unit 10, function !user + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[0], + &DetectModbusRust { + category: Some(!CodeCategory::USER_DEFINED), + unit_id: Some(10..10), + ..Default::default() + } + ), + 1 + ); + + // Force Listen Only Mode + assert_eq!( + modbus.parse( + &[ + 0x0A, 0x00, // Transaction ID + 0x00, 0x00, // Protocol ID + 0x00, 0x06, // Length + 0x00, // Unit ID + 0x08, // Function code + 0x00, 0x04, // Sub-function code + 0x00, 0x00 // Data + ], + Direction::ToServer + ), + AppLayerResult::ok() + ); + assert_eq!(modbus.transactions.len(), 2); + // function 8, subfunction 4 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[1], + &DetectModbusRust { + function: Some(FunctionCode::Diagnostic), + subfunction: Some(4), + ..Default::default() + } + ), + 1 + ); + + // Encapsulated Interface Transport (MEI) + assert_eq!( + modbus.parse( + &[ + 0x00, 0x10, // Transaction ID + 0x00, 0x00, // Protocol ID + 0x00, 0x05, // Length + 0x00, // Unit ID + 0x2B, // Function code + 0x0F, // MEI Type + 0x00, 0x00 // Data + ], + Direction::ToServer + ), + AppLayerResult::ok() + ); + assert_eq!(modbus.transactions.len(), 3); + // function reserved + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[2], + &DetectModbusRust { + category: Some(Flags::from(CodeCategory::RESERVED)), + ..Default::default() + } + ), + 1 + ); + + // Unassigned/Unknown function + assert_eq!( + modbus.parse( + &[ + 0x00, 0x0A, // Transaction ID + 0x00, 0x00, // Protocol ID + 0x00, 0x02, // Length + 0x00, // Unit ID + 0x12 // Function code + ], + Direction::ToServer + ), + AppLayerResult::ok() + ); + assert_eq!(modbus.transactions.len(), 4); + // function !assigned + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[3], + &DetectModbusRust { + category: Some(!CodeCategory::PUBLIC_ASSIGNED), + ..Default::default() + } + ), + 1 + ); + + // Read Coils request + assert_eq!( + modbus.parse( + &[ + 0x00, 0x00, // Transaction ID + 0x00, 0x00, // Protocol ID + 0x00, 0x06, // Length + 0x0a, // Unit ID + 0x01, // Function code + 0x78, 0x90, // Starting Address + 0x00, 0x13 // Quantity of coils + ], + Direction::ToServer + ), + AppLayerResult::ok() + ); + assert_eq!(modbus.transactions.len(), 5); + // access read + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[4], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + ..Default::default() + } + ), + 1 + ); + // access read, address 30870 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[4], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + address: Some(30870..30870), + ..Default::default() + } + ), + 1 + ); + // unit 10, access read, address 30863 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[4], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + unit_id: Some(10..10), + address: Some(30863..30863), + ..Default::default() + } + ), + 1 + ); + // unit 11, access read, address 30870 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[4], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + unit_id: Some(11..11), + address: Some(30870..30870), + ..Default::default() + } + ), + 1 + ); + // unit 11, access read, address 30863 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[4], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + unit_id: Some(11..11), + address: Some(30863..30863), + ..Default::default() + } + ), + 1 + ); + // unit 10, access write + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[4], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE)), + unit_id: Some(10..10), + ..Default::default() + } + ), + 1 + ); + // unit 10, access read, address 30870 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[4], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + unit_id: Some(10..10), + address: Some(30870..30870), + ..Default::default() + } + ), + 1 + ); + + // Read Inputs Register request + assert_eq!( + modbus.parse( + &[ + 0x00, 0x0A, // Transaction ID + 0x00, 0x00, // Protocol ID + 0x00, 0x06, // Length + 0x00, // Unit ID + 0x04, // Function code + 0x00, 0x08, // Starting Address + 0x00, 0x60 // Quantity of Registers + ], + Direction::ToServer + ), + AppLayerResult::ok() + ); + assert_eq!(modbus.transactions.len(), 6); + // access read input + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + ..Default::default() + } + ), + 1 + ); + // access read input, address <9 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + address: Some(std::u16::MIN..9), + ..Default::default() + } + ), + 1 + ); + // access read input, address 5<>9 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + address: Some(5..9), + ..Default::default() + } + ), + 1 + ); + // access read input, address >104 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + address: Some(104..std::u16::MAX), + ..Default::default() + } + ), + 1 + ); + // access read input, address 104<>110 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + address: Some(104..110), + ..Default::default() + } + ), + 1 + ); + // access read input, address 9 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + address: Some(9..9), + ..Default::default() + } + ), + 1 + ); + // access read input, address <10 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + address: Some(std::u16::MIN..10), + ..Default::default() + } + ), + 1 + ); + // access read input, address 5<>10 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + address: Some(5..10), + ..Default::default() + } + ), + 1 + ); + // access read input, address >103 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + address: Some(103..std::u16::MAX), + ..Default::default() + } + ), + 1 + ); + // access read input, address 103<>110 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + address: Some(103..110), + ..Default::default() + } + ), + 1 + ); + // access read input, address 104 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[5], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ | AccessType::INPUT)), + address: Some(104..104), + ..Default::default() + } + ), + 1 + ); + + // Origin: https://github.com/bro/bro/blob/master/testing/btest/Traces/modbus/modbus.trace + // Read Coils Response + assert_eq!( + modbus.parse( + &[ + 0x00, 0x01, // Transaction ID + 0x00, 0x00, // Protocol ID + 0x00, 0x04, // Length + 0x0a, // Unit ID + 0x01, // Function code + 0x01, // Count + 0x00, // Data + ], + Direction::ToClient + ), + AppLayerResult::ok() + ); + assert_eq!(modbus.transactions.len(), 7); + // function 1 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[6], + &DetectModbusRust { + function: Some(FunctionCode::RdCoils), + ..Default::default() + } + ), + 1 + ); + // access read, address 104 + // Fails because there was no request, and the address is not retrievable + // from the response. + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[6], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + address: Some(104..104), + ..Default::default() + } + ), + 1 + ); + + // Origin: https://github.com/bro/bro/blob/master/testing/btest/Traces/modbus/modbus.trace + // Write Single Register Response + assert_eq!( + modbus.parse( + &[ + 0x00, 0x01, // Transaction ID + 0x00, 0x00, // Protocol ID + 0x00, 0x06, // Length + 0x0a, // Unit ID + 0x06, // Function code + 0x00, 0x05, // Starting address + 0x00, 0x0b // Data + ], + Direction::ToClient + ), + AppLayerResult::ok() + ); + assert_eq!(modbus.transactions.len(), 8); + // function 6 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[7], + &DetectModbusRust { + function: Some(FunctionCode::WrSingleReg), + ..Default::default() + } + ), + 1 + ); + // access write, address 10 + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[7], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::WRITE)), + address: Some(10..10), + ..Default::default() + } + ), + 1 + ); + + // Origin: https://github.com/bro/bro/blob/master/testing/btest/Traces/modbus/modbus.trace + // Write Single Register Response + assert_eq!( + modbus.parse( + &[ + 0x00, 0x00, // Transaction ID + 0x00, 0x00, // Protocol ID + 0x00, 0x06, // Length + 0x0a, // Unit ID + 0x08, // Function code + 0x00, 0x0a, // Diagnostic code + 0x00, 0x00 // Data + ], + Direction::ToClient + ), + AppLayerResult::ok() + ); + assert_eq!(modbus.transactions.len(), 9); + // function 8 + assert_eq!( + rs_modbus_inspect( + &modbus.transactions[8], + &DetectModbusRust { + function: Some(FunctionCode::Diagnostic), + ..Default::default() + } + ), + 1 + ); + // access read + assert_ne!( + rs_modbus_inspect( + &modbus.transactions[8], + &DetectModbusRust { + access_type: Some(Flags::from(AccessType::READ)), + ..Default::default() + } + ), + 1 + ); + } +} diff --git a/src/Makefile.am b/src/Makefile.am index 468da030b9..246249f555 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -143,7 +143,6 @@ noinst_HEADERS = \ detect-engine.h \ detect-engine-iponly.h \ detect-engine-loader.h \ - detect-engine-modbus.h \ detect-engine-mpm.h \ detect-engine-payload.h \ detect-engine-port.h \ @@ -722,7 +721,6 @@ libsuricata_c_a_SOURCES = \ detect-engine-file.c \ detect-engine-iponly.c \ detect-engine-loader.c \ - detect-engine-modbus.c \ detect-engine-mpm.c \ detect-engine-payload.c \ detect-engine-port.c \ diff --git a/src/app-layer-modbus.h b/src/app-layer-modbus.h index 5d97fdcede..04a12bcee9 100644 --- a/src/app-layer-modbus.h +++ b/src/app-layer-modbus.h @@ -34,32 +34,6 @@ #ifndef __APP_LAYER_MODBUS_H__ #define __APP_LAYER_MODBUS_H__ -/* Modbus Function Code Categories. */ -#define MODBUS_CAT_NONE 0x0 -#define MODBUS_CAT_PUBLIC_ASSIGNED (1<<0) -#define MODBUS_CAT_PUBLIC_UNASSIGNED (1<<1) -#define MODBUS_CAT_USER_DEFINED (1<<2) -#define MODBUS_CAT_RESERVED (1<<3) -#define MODBUS_CAT_ALL 0xFF - -/* Modbus Read/Write function and Access Types. */ -#define MODBUS_TYP_NONE 0x0 -#define MODBUS_TYP_ACCESS_MASK 0x03 -#define MODBUS_TYP_READ (1<<0) -#define MODBUS_TYP_WRITE (1<<1) -#define MODBUS_TYP_ACCESS_FUNCTION_MASK 0x3C -#define MODBUS_TYP_BIT_ACCESS_MASK 0x0C -#define MODBUS_TYP_DISCRETES (1<<2) -#define MODBUS_TYP_COILS (1<<3) -#define MODBUS_TYP_WORD_ACCESS_MASK 0x30 -#define MODBUS_TYP_INPUT (1<<4) -#define MODBUS_TYP_HOLDING (1<<5) -#define MODBUS_TYP_SINGLE (1<<6) -#define MODBUS_TYP_MULTIPLE (1<<7) -#define MODBUS_TYP_WRITE_SINGLE (MODBUS_TYP_WRITE | MODBUS_TYP_SINGLE) -#define MODBUS_TYP_WRITE_MULTIPLE (MODBUS_TYP_WRITE | MODBUS_TYP_MULTIPLE) -#define MODBUS_TYP_READ_WRITE_MULTIPLE (MODBUS_TYP_READ | MODBUS_TYP_WRITE | MODBUS_TYP_MULTIPLE) - void RegisterModbusParsers(void); #endif /* __APP_LAYER_MODBUS_H__ */ diff --git a/src/detect-engine-modbus.c b/src/detect-engine-modbus.c deleted file mode 100644 index 354a73c707..0000000000 --- a/src/detect-engine-modbus.c +++ /dev/null @@ -1,1237 +0,0 @@ -/* - * Copyright (C) 2014 ANSSI - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** \file - * - * \author David DIALLO - * - * Based on detect-engine-dns.c - */ - -#include "suricata-common.h" - -#include "app-layer.h" - -#include "detect.h" -#include "detect-modbus.h" - -#include "detect-engine-modbus.h" - -#include "flow.h" - -#include "util-debug.h" - -#ifdef UNITTESTS /* UNITTESTS */ -#include "app-layer-parser.h" - -#include "detect-parse.h" - -#include "detect-engine.h" - -#include "flow-util.h" - -#include "stream-tcp.h" - -#include "util-unittest.h" -#include "util-unittest-helper.h" - -/* Modbus Application Protocol Specification V1.1b3 6.1: Read Coils */ -/* Example of a request to read discrete outputs 20-38 */ -static uint8_t readCoilsReq[] = {/* Transaction ID */ 0x00, 0x00, - /* Protocol ID */ 0x00, 0x00, - /* Length */ 0x00, 0x06, - /* Unit ID */ 0x0a, - /* Function code */ 0x01, - /* Starting Address */ 0x78, 0x90, - /* Quantity of coils */ 0x00, 0x13 }; - -/* Modbus Application Protocol Specification V1.1b3 6.4: Read Input Registers */ -/* Example of a request to read input register 9 */ -static uint8_t readInputsRegistersReq[] = {/* Transaction ID */ 0x00, 0x0A, - /* Protocol ID */ 0x00, 0x00, - /* Length */ 0x00, 0x06, - /* Unit ID */ 0x00, - /* Function code */ 0x04, - /* Starting Address */ 0x00, 0x08, - /* Quantity of Registers */ 0x00, 0x60}; - -/* Modbus Application Protocol Specification V1.1b3 6.17: Read/Write Multiple registers */ -/* Example of a request to read six registers starting at register 4, */ -/* and to write three registers starting at register 15 */ -static uint8_t readWriteMultipleRegistersReq[] = {/* Transaction ID */ 0x12, 0x34, - /* Protocol ID */ 0x00, 0x00, - /* Length */ 0x00, 0x11, - /* Unit ID */ 0x0a, - /* Function code */ 0x17, - /* Read Starting Address */ 0x00, 0x03, - /* Quantity to Read */ 0x00, 0x06, - /* Write Starting Address */ 0x00, 0x0E, - /* Quantity to Write */ 0x00, 0x03, - /* Write Byte count */ 0x06, - /* Write Registers Value */ 0x12, 0x34, /* 15 */ - 0x56, 0x78, /* 16 */ - 0x9A, 0xBC};/* 17 */ - -/* Modbus Application Protocol Specification V1.1b3 6.8.1: 04 Force Listen Only Mode */ -/* Example of a request to to remote device to its Listen Only MOde for Modbus Communications. */ -static uint8_t forceListenOnlyMode[] = {/* Transaction ID */ 0x0A, 0x00, - /* Protocol ID */ 0x00, 0x00, - /* Length */ 0x00, 0x06, - /* Unit ID */ 0x00, - /* Function code */ 0x08, - /* Sub-function code */ 0x00, 0x04, - /* Data */ 0x00, 0x00}; - -/* Modbus Application Protocol Specification V1.1b3 Annex A */ -/* Modbus Reserved Function codes, Subcodes and MEI types */ -static uint8_t encapsulatedInterfaceTransport[] = { - /* Transaction ID */ 0x00, 0x10, - /* Protocol ID */ 0x00, 0x00, - /* Length */ 0x00, 0x05, - /* Unit ID */ 0x00, - /* Function code */ 0x2B, - /* MEI Type */ 0x0F, - /* Data */ 0x00, 0x00}; - -static uint8_t unassigned[] = { - /* Transaction ID */ 0x00, 0x0A, - /* Protocol ID */ 0x00, 0x00, - /* Length */ 0x00, 0x02, - /* Unit ID */ 0x00, - /* Function code */ 0x3F -}; - -/** \test Test code function. */ -static int DetectEngineInspectModbusTest01(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code function\"; " - "modbus: function 23; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, - readWriteMultipleRegistersReq, - sizeof(readWriteMultipleRegistersReq)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF_NOT(PacketAlertCheck(p, 1)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test code function and code subfunction. */ -static int DetectEngineInspectModbusTest02(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus function and subfunction\"; " - "modbus: function 8, subfunction 4; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, forceListenOnlyMode, - sizeof(forceListenOnlyMode)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF_NOT(PacketAlertCheck(p, 1)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test function category. */ -static int DetectEngineInspectModbusTest03(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus category function\"; " - "modbus: function reserved; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, - encapsulatedInterfaceTransport, - sizeof(encapsulatedInterfaceTransport)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF_NOT(PacketAlertCheck(p, 1)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test negative function category. */ -static int DetectEngineInspectModbusTest04(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus category function\"; " - "modbus: function !assigned; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, unassigned, - sizeof(unassigned)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF_NOT(PacketAlertCheck(p, 1)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test access type. */ -static int DetectEngineInspectModbusTest05(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access type\"; " - "modbus: access read; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, readCoilsReq, - sizeof(readCoilsReq)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF_NOT(PacketAlertCheck(p, 1)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test access function. */ -static int DetectEngineInspectModbusTest06(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access type\"; " - "modbus: access read input; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, readInputsRegistersReq, - sizeof(readInputsRegistersReq)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF_NOT(PacketAlertCheck(p, 1)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test read access at an address. */ -static int DetectEngineInspectModbusTest07(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus address access\"; " - "modbus: access read, address 30870; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, readCoilsReq, - sizeof(readCoilsReq)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF_NOT(PacketAlertCheck(p, 1)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test read access at a range of address. */ -static int DetectEngineInspectModbusTest08(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - /* readInputsRegistersReq, Starting Address = 0x08, Quantity of Registers = 0x60 */ - /* Read access address from 9 to 104 */ - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access\"; " - "modbus: access read input, " - "address <9; sid:1;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access\"; " - "modbus: access read input, " - "address 9; sid:2;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access\"; " - "modbus: access read input, " - "address 5<>9; sid:3;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access\"; " - "modbus: access read input, " - "address <10; sid:4;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access\"; " - "modbus: access read input, " - "address 5<>10; sid:5;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access\"; " - "modbus: access read input, " - "address >103; sid:6;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access\"; " - "modbus: access read input, " - "address 103<>110; sid:7;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access\"; " - "modbus: access read input, " - "address 104; sid:8;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access\"; " - "modbus: access read input, " - "address >104; sid:9;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus access\"; " - "modbus: access read input, " - "address 104<>110; sid:10;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, readInputsRegistersReq, - sizeof(readInputsRegistersReq)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 1)); - FAIL_IF(PacketAlertCheck(p, 3)); - FAIL_IF(PacketAlertCheck(p, 9)); - FAIL_IF(PacketAlertCheck(p, 10)); - - FAIL_IF_NOT(PacketAlertCheck(p, 2)); - FAIL_IF_NOT(PacketAlertCheck(p, 4)); - FAIL_IF_NOT(PacketAlertCheck(p, 5)); - FAIL_IF_NOT(PacketAlertCheck(p, 6)); - FAIL_IF_NOT(PacketAlertCheck(p, 7)); - FAIL_IF_NOT(PacketAlertCheck(p, 8)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test write access at a address in a range of value. */ -static int DetectEngineInspectModbusTest09(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - /* readWriteMultipleRegistersReq, Write Starting Address = 0x0E, Quantity to Write = 0x03 */ - /* Write access register address 15 = 0x1234 (4660) */ - /* Write access register address 16 = 0x5678 (22136) */ - /* Write access register address 17 = 0x9ABC (39612) */ - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus write access\"; " - "modbus: access write holding, " - "address 15, value <4660; sid:1;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus write access\"; " - "modbus: access write holding, " - "address 16, value <22137; sid:2;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus write access\"; " - "modbus: access write holding, " - "address 17, value 39612; sid:3;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus write access\"; " - "modbus: access write holding, " - "address 15, value 4661; sid:4;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus write access\"; " - "modbus: access write holding, " - "address 16, value 20000<>22136; sid:5;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus write access\"; " - "modbus: access write holding, " - "address 17, value 30000<>39613; sid:6;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus write access\"; " - "modbus: access write holding, " - "address 15, value 4659<>5000; sid:7;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus write access\"; " - "modbus: access write holding, " - "address 16, value 22136<>30000; sid:8;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus write access\"; " - "modbus: access write holding, " - "address 17, value >39611; sid:9;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus write access\"; " - "modbus: access write holding, " - "address 15, value >4660; sid:10;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, - readWriteMultipleRegistersReq, - sizeof(readWriteMultipleRegistersReq)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 1)); - FAIL_IF(PacketAlertCheck(p, 4)); - FAIL_IF(PacketAlertCheck(p, 5)); - FAIL_IF(PacketAlertCheck(p, 8)); - FAIL_IF(PacketAlertCheck(p, 10)); - - FAIL_IF_NOT(PacketAlertCheck(p, 2)); - FAIL_IF_NOT(PacketAlertCheck(p, 3)); - FAIL_IF_NOT(PacketAlertCheck(p, 6)); - FAIL_IF_NOT(PacketAlertCheck(p, 7)); - FAIL_IF_NOT(PacketAlertCheck(p, 9)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test Test code unit_id. */ -static int DetectEngineInspectModbusTest10(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readWriteMultipleRegistersReq, - sizeof(readWriteMultipleRegistersReq), - IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - /* readWriteMultipleRegistersReq, Write Starting Address = 0x0E, Quantity to Write = 0x03 */ - /* Unit ID = 0x0a (10) */ - /* Function code = 0x17 (23) */ - /* Write access register address 15 = 0x1234 (4660) */ - /* Write access register address 16 = 0x5678 (22136) */ - /* Write access register address 17 = 0x9ABC (39612) */ - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 10; sid:1;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 12; sid:2;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 5<>15; sid:3;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 5<>9; sid:4;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 11<>15; sid:5;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit >9; sid:6;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit >11; sid:7;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit <11; sid:8;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit <9; sid:9;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, - readWriteMultipleRegistersReq, - sizeof(readWriteMultipleRegistersReq)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 2)); - FAIL_IF(PacketAlertCheck(p, 4)); - FAIL_IF(PacketAlertCheck(p, 5)); - FAIL_IF(PacketAlertCheck(p, 7)); - FAIL_IF(PacketAlertCheck(p, 9)); - - FAIL_IF_NOT(PacketAlertCheck(p, 1)); - FAIL_IF_NOT(PacketAlertCheck(p, 3)); - FAIL_IF_NOT(PacketAlertCheck(p, 6)); - FAIL_IF_NOT(PacketAlertCheck(p, 8)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test Test code unit_id and code function. */ -static int DetectEngineInspectModbusTest11(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readWriteMultipleRegistersReq, - sizeof(readWriteMultipleRegistersReq), - IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - /* readWriteMultipleRegistersReq, Write Starting Address = 0x0E, Quantity to Write = 0x03 */ - /* Unit ID = 0x0a (10) */ - /* Function code = 0x17 (23) */ - /* Write access register address 15 = 0x1234 (4660) */ - /* Write access register address 16 = 0x5678 (22136) */ - /* Write access register address 17 = 0x9ABC (39612) */ - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 10, function 20; sid:1;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 10, function 23; sid:2;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 11, function 20; sid:3;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 11, function 23; sid:4;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 10, function public; sid:5;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 11, function public; sid:6;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 10, function user; sid:7;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus code unit_id\"; " - "modbus: unit 10, function !user; sid:8;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, - readWriteMultipleRegistersReq, - sizeof(readWriteMultipleRegistersReq)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 1)); - FAIL_IF(PacketAlertCheck(p, 3)); - FAIL_IF(PacketAlertCheck(p, 4)); - FAIL_IF(PacketAlertCheck(p, 6)); - FAIL_IF(PacketAlertCheck(p, 7)); - - FAIL_IF_NOT(PacketAlertCheck(p, 2)); - FAIL_IF_NOT(PacketAlertCheck(p, 5)); - FAIL_IF_NOT(PacketAlertCheck(p, 8)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} - -/** \test unit_id and read access at an address. */ -static int DetectEngineInspectModbusTest12(void) -{ - AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); - DetectEngineThreadCtx *det_ctx = NULL; - DetectEngineCtx *de_ctx = NULL; - Flow f; - Packet *p = NULL; - Signature *s = NULL; - TcpSession ssn; - ThreadVars tv; - - FAIL_IF_NULL(alp_tctx); - - memset(&tv, 0, sizeof(ThreadVars)); - memset(&f, 0, sizeof(Flow)); - memset(&ssn, 0, sizeof(TcpSession)); - - p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); - - FLOW_INITIALIZE(&f); - f.alproto = ALPROTO_MODBUS; - f.protoctx = (void *)&ssn; - f.proto = IPPROTO_TCP; - f.flags |= FLOW_IPV4; - - p->flow = &f; - p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; - p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - - StreamTcpInitConfig(true); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - /* readCoilsReq, Read coils Starting Address = 0x7890 (30864), Quantity of coils = 0x13 (19) */ - /* Unit ID = 0x0a (10) */ - /* Function code = 0x01 (01) */ - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus address access\"; " - "modbus: unit 10, access read, address 30870; sid:1;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus address access\"; " - "modbus: unit 10, access read, address 30863; sid:2;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus address access\"; " - "modbus: unit 11, access read, address 30870; sid:3;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus address access\"; " - "modbus: unit 11, access read, address 30863; sid:4;)"); - - s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus address access\"; " - "modbus: unit 10, access write; sid:5;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - - FLOWLOCK_WRLOCK(&f); - int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, - STREAM_TOSERVER, readCoilsReq, - sizeof(readCoilsReq)); - FAIL_IF_NOT(r == 0); - FLOWLOCK_UNLOCK(&f); - - FAIL_IF_NULL(f.alstate); - - /* do detect */ - SigMatchSignatures(&tv, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 2)); - FAIL_IF(PacketAlertCheck(p, 3)); - FAIL_IF(PacketAlertCheck(p, 4)); - FAIL_IF(PacketAlertCheck(p, 5)); - - FAIL_IF_NOT(PacketAlertCheck(p, 1)); - - AppLayerParserThreadCtxFree(alp_tctx); - DetectEngineThreadCtxDeinit(&tv, det_ctx); - SigGroupCleanup(de_ctx); - DetectEngineCtxFree(de_ctx); - - StreamTcpFreeConfig(true); - FLOW_DESTROY(&f); - UTHFreePacket(p); - PASS; -} -#endif /* UNITTESTS */ - -void DetectEngineInspectModbusRegisterTests(void) -{ -#ifdef UNITTESTS - UtRegisterTest("DetectEngineInspectModbusTest01 - Code function", - DetectEngineInspectModbusTest01); - UtRegisterTest("DetectEngineInspectModbusTest02 - code function and code subfunction", - DetectEngineInspectModbusTest02); - UtRegisterTest("DetectEngineInspectModbusTest03 - Function category", - DetectEngineInspectModbusTest03); - UtRegisterTest("DetectEngineInspectModbusTest04 - Negative function category", - DetectEngineInspectModbusTest04); - UtRegisterTest("DetectEngineInspectModbusTest05 - Access type", - DetectEngineInspectModbusTest05); - UtRegisterTest("DetectEngineInspectModbusTest06 - Access function", - DetectEngineInspectModbusTest06); - UtRegisterTest("DetectEngineInspectModbusTest07 - Read access at an address", - DetectEngineInspectModbusTest07); - UtRegisterTest("DetectEngineInspectModbusTest08 - Read access at a range of address", - DetectEngineInspectModbusTest08); - UtRegisterTest("DetectEngineInspectModbusTest09 - Write access at an address a range of value", - DetectEngineInspectModbusTest09); - UtRegisterTest("DetectEngineInspectModbusTest10 - Code unit_id", - DetectEngineInspectModbusTest10); - UtRegisterTest("DetectEngineInspectModbusTest11 - Code unit_id and code function", - DetectEngineInspectModbusTest11); - UtRegisterTest("DetectEngineInspectModbusTest12 - Code unit_id and acces function", - DetectEngineInspectModbusTest12); -#endif /* UNITTESTS */ - return; -} diff --git a/src/detect-engine-modbus.h b/src/detect-engine-modbus.h deleted file mode 100644 index 5881fa772b..0000000000 --- a/src/detect-engine-modbus.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2014 ANSSI - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** \file - * - * \author David DIALLO - */ - -#ifndef __DETECT_ENGINE_MODBUS_H__ -#define __DETECT_ENGINE_MODBUS_H__ - -int DetectEngineInspectModbus(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *, - const struct DetectEngineAppInspectionEngine_ *engine, const Signature *, Flow *, uint8_t, - void *, void *, uint64_t); - -void DetectEngineInspectModbusRegisterTests(void); -#endif /* __DETECT_ENGINE_MODBUS_H__ */ diff --git a/src/detect-modbus.c b/src/detect-modbus.c index 0fe6d345f8..6603ffbf37 100644 --- a/src/detect-modbus.c +++ b/src/detect-modbus.c @@ -52,8 +52,6 @@ #include "util-debug.h" #include "util-byte.h" -#include "app-layer-modbus.h" - #include "stream-tcp.h" #include "rust.h" @@ -170,502 +168,170 @@ void DetectModbusRegister(void) } #ifdef UNITTESTS /* UNITTESTS */ -#include "util-unittest.h" - -/** Convert rust structure to C for regression tests. - * - * Note: Newly allocated `DetectModbus` structure must be freed. - * - * TODO: remove this after regression testing commit. - */ -static DetectModbusValue *DetectModbusValueRustToC(uint16_t min, uint16_t max) -{ - DetectModbusValue *value = SCMalloc(sizeof(*value)); - FAIL_IF_NULL(value); - - value->min = min; - value->max = max; - - if (min == max) { - value->mode = DETECT_MODBUS_EQ; - } else if (min == 0) { - value->mode = DETECT_MODBUS_LT; - } else if (max == UINT16_MAX) { - value->mode = DETECT_MODBUS_GT; - } else { - value->mode = DETECT_MODBUS_RA; - } - - return value; -} - -static DetectModbus *DetectModbusRustToC(DetectModbusRust *ctx) -{ - DetectModbus *modbus = SCMalloc(sizeof(*modbus)); - FAIL_IF_NULL(modbus); - - modbus->category = rs_modbus_get_category(ctx); - modbus->function = rs_modbus_get_function(ctx); - modbus->subfunction = rs_modbus_get_subfunction(ctx); - modbus->has_subfunction = rs_modbus_get_has_subfunction(ctx); - modbus->type = rs_modbus_get_access_type(ctx); - - modbus->unit_id = DetectModbusValueRustToC( - rs_modbus_get_unit_id_min(ctx), rs_modbus_get_unit_id_max(ctx)); - - modbus->address = DetectModbusValueRustToC( - rs_modbus_get_address_min(ctx), rs_modbus_get_address_max(ctx)); - - modbus->data = - DetectModbusValueRustToC(rs_modbus_get_data_min(ctx), rs_modbus_get_data_max(ctx)); - - return modbus; -} - -static void DetectModbusCFree(DetectModbus *modbus) -{ - if (modbus) { - if (modbus->unit_id) - SCFree(modbus->unit_id); - - if (modbus->address) - SCFree(modbus->address); - - if (modbus->data) - SCFree(modbus->data); - - SCFree(modbus); - } -} - -/** \test Signature containing a function. */ -static int DetectModbusTest01(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus function\"; " - "modbus: function 1; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); +#include "app-layer-parser.h" - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); +#include "flow-util.h" - FAIL_IF_NOT(modbus->function == 1); - - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); - PASS; -} +#include "util-unittest.h" +#include "util-unittest-helper.h" -/** \test Signature containing a function and a subfunction. */ -static int DetectModbusTest02(void) +/** + * Sample data for tests derived from + * https://github.com/bro/bro/blob/master/testing/btest/Traces/modbus/modbus.trace + */ +static uint8_t writeSingleCoil[] = { + /* Transaction ID */ 0x00, 0x01, + /* Protocol ID */ 0x00, 0x00, + /* Length */ 0x00, 0x06, + /* Unit ID */ 0x0a, + /* Function code */ 0x05, + /* Read Starting Address */ 0x00, 0x02, + /* Data */ 0x00, 0x00 +}; + +static uint8_t restartCommOption[] = { + /* Transaction ID */ 0x00, 0x00, + /* Protocol ID */ 0x00, 0x00, + /* Length */ 0x00, 0x06, + /* Unit ID */ 0x0a, + /* Function code */ 0x08, + /* Diagnostic Code */ 0x00, 0x01, + /* Data */ 0x00, 0x00 +}; + +/** \test Signature containing an access type. */ +static int DetectModbusTestAccess(void) { + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + DetectEngineThreadCtx *det_ctx = NULL; DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); + Flow f; + Packet *p = NULL; + Signature *s = NULL; + TcpSession ssn; + ThreadVars tv; - de_ctx->flags |= DE_QUIET; + FAIL_IF_NULL(alp_tctx); - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus function and subfunction\"; " - "modbus: function 8, subfunction 4; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + memset(&tv, 0, sizeof(ThreadVars)); + memset(&f, 0, sizeof(Flow)); + memset(&ssn, 0, sizeof(TcpSession)); - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + p = UTHBuildPacket(restartCommOption, sizeof(restartCommOption), IPPROTO_TCP); - FAIL_IF_NOT(modbus->function == 8); - FAIL_IF_NOT(modbus->subfunction == 4); + FLOW_INITIALIZE(&f); + f.alproto = ALPROTO_MODBUS; + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); - PASS; -} + p->flow = &f; + p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; -/** \test Signature containing a function category. */ -static int DetectModbusTest03(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; + StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; + s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code function\"; " + "modbus: access write; sid:1;)"); + FAIL_IF_NULL(s); - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus.function\"; " - "modbus: function reserved; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + FLOWLOCK_WRLOCK(&f); + int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, + writeSingleCoil, sizeof(writeSingleCoil)); + FAIL_IF_NOT(r == 0); + FLOWLOCK_UNLOCK(&f); - FAIL_IF_NOT(modbus->category == MODBUS_CAT_RESERVED); + FAIL_IF_NULL(f.alstate); - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); - PASS; -} + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); -/** \test Signature containing a negative function category. */ -static int DetectModbusTest04(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus function\"; " - "modbus: function !assigned; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - FAIL_IF(modbus->category & MODBUS_CAT_PUBLIC_ASSIGNED); - FAIL_IF_NOT(modbus->category & MODBUS_CAT_PUBLIC_UNASSIGNED); - FAIL_IF_NOT(modbus->category & MODBUS_CAT_USER_DEFINED); - FAIL_IF_NOT(modbus->category & MODBUS_CAT_RESERVED); - - DetectModbusCFree(modbus); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); - PASS; -} -/** \test Signature containing a access type. */ -static int DetectModbusTest05(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus.access\"; " - "modbus: access read; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - FAIL_IF_NOT(modbus->type == MODBUS_TYP_READ); - - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); PASS; } -/** \test Signature containing a access function. */ -static int DetectModbusTest06(void) +/** \test Signature containing a function. */ +static int DetectModbusTestFunction(void) { + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + DetectEngineThreadCtx *det_ctx = NULL; DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - - uint8_t type = (MODBUS_TYP_READ | MODBUS_TYP_DISCRETES); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus.access\"; " - "modbus: access read discretes; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - FAIL_IF_NOT(modbus->type == type); + Flow f; + Packet *p = NULL; + Signature *s = NULL; + TcpSession ssn; + ThreadVars tv; - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); - PASS; -} - -/** \test Signature containing a read access at an address. */ -static int DetectModbusTest07(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - DetectModbusMode mode = DETECT_MODBUS_EQ; - - uint8_t type = MODBUS_TYP_READ; - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus.access\"; " - "modbus: access read, address 1000; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - FAIL_IF_NOT(modbus->type == type); - FAIL_IF_NOT((*modbus->address).mode == mode); - FAIL_IF_NOT((*modbus->address).min == 1000); - - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); - PASS; -} - -/** \test Signature containing a write access at a range of address. */ -static int DetectModbusTest08(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - DetectModbusMode mode = DETECT_MODBUS_GT; - - uint8_t type = (MODBUS_TYP_WRITE | MODBUS_TYP_COILS); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus.access\"; " - "modbus: access write coils, address >500; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - FAIL_IF_NOT(modbus->type == type); - FAIL_IF_NOT((*modbus->address).mode == mode); - FAIL_IF_NOT((*modbus->address).min == 500); - - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); - PASS; -} - -/** \test Signature containing a write access at a address a range of value. */ -static int DetectModbusTest09(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - DetectModbusMode addressMode = DETECT_MODBUS_EQ; - DetectModbusMode valueMode = DETECT_MODBUS_RA; - - uint8_t type = (MODBUS_TYP_WRITE | MODBUS_TYP_HOLDING); - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus.access\"; " - "modbus: access write holding, address 100, value 500<>1000; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + FAIL_IF_NULL(alp_tctx); - FAIL_IF_NOT(modbus->type == type); - FAIL_IF_NOT((*modbus->address).mode == addressMode); - FAIL_IF_NOT((*modbus->address).min == 100); - FAIL_IF_NOT((*modbus->data).mode == valueMode); - FAIL_IF_NOT((*modbus->data).min == 500); - FAIL_IF_NOT((*modbus->data).max == 1000); + memset(&tv, 0, sizeof(ThreadVars)); + memset(&f, 0, sizeof(Flow)); + memset(&ssn, 0, sizeof(TcpSession)); - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); - PASS; -} - -/** \test Signature containing a unit_id. */ -static int DetectModbusTest10(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - DetectModbusMode mode = DETECT_MODBUS_EQ; + p = UTHBuildPacket(writeSingleCoil, sizeof(writeSingleCoil), IPPROTO_TCP); - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus unit_id\"; " - "modbus: unit 10; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + FLOW_INITIALIZE(&f); + f.alproto = ALPROTO_MODBUS; + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + p->flow = &f; + p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; - FAIL_IF_NOT((*modbus->unit_id).min == 10); - FAIL_IF_NOT((*modbus->unit_id).mode == mode); - - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); - PASS; -} - -/** \test Signature containing a unit_id, a function and a subfunction. */ -static int DetectModbusTest11(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - DetectModbusMode mode = DETECT_MODBUS_EQ; + StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; + s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code function\"; " + "modbus: function 8; sid:1;)"); + FAIL_IF_NULL(s); - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus function and subfunction\"; " - "modbus: unit 10, function 8, subfunction 4; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - FAIL_IF_NOT((*modbus->unit_id).min == 10); - FAIL_IF_NOT((*modbus->unit_id).mode == mode); - FAIL_IF_NOT(modbus->function == 8); - FAIL_IF_NOT(modbus->subfunction == 4); - - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); - PASS; -} - -/** \test Signature containing an unit_id and a read access at an address. */ -static int DetectModbusTest12(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - DetectModbusMode mode = DETECT_MODBUS_EQ; - - uint8_t type = MODBUS_TYP_READ; - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); - de_ctx->flags |= DE_QUIET; + FLOWLOCK_WRLOCK(&f); + int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, + restartCommOption, sizeof(restartCommOption)); + FAIL_IF_NOT(r == 0); + FLOWLOCK_UNLOCK(&f); - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus.access\"; " - "modbus: unit 10, access read, address 1000; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + FAIL_IF_NULL(f.alstate); - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); - FAIL_IF_NOT((*modbus->unit_id).min == 10); - FAIL_IF_NOT((*modbus->unit_id).mode == mode); - FAIL_IF_NOT(modbus->type == type); - FAIL_IF_NOT((*modbus->address).mode == mode); - FAIL_IF_NOT((*modbus->address).min == 1000); + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - DetectModbusCFree(modbus); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); - PASS; -} -/** \test Signature containing a range of unit_id. */ -static int DetectModbusTest13(void) -{ - DetectEngineCtx *de_ctx = NULL; - DetectModbus *modbus = NULL; - DetectModbusMode mode = DETECT_MODBUS_RA; - - de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " - "(msg:\"Testing modbus.access\"; " - "modbus: unit 10<>500; sid:1;)"); - FAIL_IF_NULL(de_ctx->sig_list); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); - FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - modbus = DetectModbusRustToC( - (DetectModbusRust *)de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - - FAIL_IF_NOT((*modbus->unit_id).min == 10); - FAIL_IF_NOT((*modbus->unit_id).max == 500); - FAIL_IF_NOT((*modbus->unit_id).mode == mode); - - DetectModbusCFree(modbus); - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); - DetectEngineCtxFree(de_ctx); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); PASS; } @@ -674,31 +340,7 @@ static int DetectModbusTest13(void) */ void DetectModbusRegisterTests(void) { - UtRegisterTest("DetectModbusTest01 - Testing function", - DetectModbusTest01); - UtRegisterTest("DetectModbusTest02 - Testing function and subfunction", - DetectModbusTest02); - UtRegisterTest("DetectModbusTest03 - Testing category function", - DetectModbusTest03); - UtRegisterTest("DetectModbusTest04 - Testing category function in negative", - DetectModbusTest04); - UtRegisterTest("DetectModbusTest05 - Testing access type", - DetectModbusTest05); - UtRegisterTest("DetectModbusTest06 - Testing access function", - DetectModbusTest06); - UtRegisterTest("DetectModbusTest07 - Testing access at address", - DetectModbusTest07); - UtRegisterTest("DetectModbusTest08 - Testing a range of address", - DetectModbusTest08); - UtRegisterTest("DetectModbusTest09 - Testing write a range of value", - DetectModbusTest09); - UtRegisterTest("DetectModbusTest10 - Testing unit_id", - DetectModbusTest10); - UtRegisterTest("DetectModbusTest11 - Testing unit_id, function and subfunction", - DetectModbusTest11); - UtRegisterTest("DetectModbusTest12 - Testing unit_id and access at address", - DetectModbusTest12); - UtRegisterTest("DetectModbusTest13 - Testing a range of unit_id", - DetectModbusTest13); + UtRegisterTest("DetectModbusTestAccess", DetectModbusTestAccess); + UtRegisterTest("DetectModbusTestFunction", DetectModbusTestFunction); } #endif /* UNITTESTS */ diff --git a/src/detect-modbus.h b/src/detect-modbus.h index 2686e21f65..9c96ddeef8 100644 --- a/src/detect-modbus.h +++ b/src/detect-modbus.h @@ -34,32 +34,6 @@ #ifndef __DETECT_MODBUS_H__ #define __DETECT_MODBUS_H__ -#include "app-layer-modbus.h" - -typedef enum { - DETECT_MODBUS_EQ = 0, /** < EQual operator */ - DETECT_MODBUS_LT, /** < "Less Than" operator */ - DETECT_MODBUS_GT, /** < "Greater Than" operator */ - DETECT_MODBUS_RA, /** < RAnge operator */ -} DetectModbusMode; - -typedef struct DetectModbusValue_ { - uint16_t min; /** < Modbus minimum [range] or equal value to match */ - uint16_t max; /** < Modbus maximum value [range] to match */ - DetectModbusMode mode; /** < Modbus operator used in the address/data signature */ -} DetectModbusValue; - -typedef struct DetectModbus_ { - uint8_t category; /** < Modbus function code category to match */ - uint8_t function; /** < Modbus function code to match */ - uint16_t subfunction; /** < Modbus subfunction to match */ - bool has_subfunction; /** < Modbus subfunction indicator */ - uint8_t type; /** < Modbus access type to match */ - DetectModbusValue *unit_id; /** < Modbus unit id to match */ - DetectModbusValue *address; /** < Modbus address to match */ - DetectModbusValue *data; /** < Modbus data to match */ -} DetectModbus; - /* prototypes */ void DetectModbusRegister(void); diff --git a/src/runmode-unittests.c b/src/runmode-unittests.c index e6617c307f..1ed21f4b23 100644 --- a/src/runmode-unittests.c +++ b/src/runmode-unittests.c @@ -37,7 +37,6 @@ #include "detect-engine-dcepayload.h" #include "detect-engine-state.h" #include "detect-engine-tag.h" -#include "detect-engine-modbus.h" #include "detect-fast-pattern.h" #include "flow.h" #include "flow-timeout.h" @@ -184,7 +183,6 @@ static void RegisterUnittests(void) #endif DeStateRegisterTests(); MemcmpRegisterTests(); - DetectEngineInspectModbusRegisterTests(); DetectEngineRegisterTests(); SCLogRegisterTests(); MagicRegisterTests();