detect/integers: make mqtt use generic detect_parse_uint_bitflags

Ticket: 6724
pull/13910/head
Philippe Antoine 2 months ago
parent 867f5bfa21
commit c1917dec21

@ -19,8 +19,9 @@
use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER}; use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER};
use crate::detect::uint::{ use crate::detect::uint::{
detect_match_uint, detect_parse_array_uint_enum, detect_parse_uint, detect_uint_match_at_index, detect_match_uint, detect_parse_array_uint_enum, detect_parse_uint_bitflags,
DetectUintArrayData, DetectUintData, DetectUintMode, SCDetectU8Free, SCDetectU8Parse, detect_uint_match_at_index, DetectUintArrayData, DetectUintData, SCDetectU8Free,
SCDetectU8Parse,
}; };
use crate::detect::{ use crate::detect::{
helper_keyword_register_multi_buffer, helper_keyword_register_sticky_buffer, helper_keyword_register_multi_buffer, helper_keyword_register_sticky_buffer,
@ -34,12 +35,6 @@ use suricata_sys::sys::{
SCSigTableAppLiteElmt, SigMatchCtx, Signature, SCSigTableAppLiteElmt, SigMatchCtx, Signature,
}; };
use nom7::branch::alt;
use nom7::bytes::complete::{is_a, tag};
use nom7::combinator::{opt, value};
use nom7::multi::many1;
use nom7::IResult;
use super::mqtt::{MQTTState, MQTTTransaction, ALPROTO_MQTT}; use super::mqtt::{MQTTState, MQTTTransaction, ALPROTO_MQTT};
use crate::conf::conf_get; use crate::conf::conf_get;
use crate::mqtt::mqtt_message::{MQTTMessage, MQTTOperation, MQTTTypeCode}; use crate::mqtt::mqtt_message::{MQTTMessage, MQTTOperation, MQTTTypeCode};
@ -672,56 +667,11 @@ unsafe extern "C" fn mqtt_protocol_version_free(_de: *mut DetectEngineCtx, ctx:
SCDetectU8Free(ctx); SCDetectU8Free(ctx);
} }
// maybe to factor with websocket.flags #[repr(u8)]
struct MqttParsedFlagItem { #[derive(EnumStringU8)]
neg: bool, pub enum MqttFlag {
value: u8, Dup = 0x8,
} Retain = 0x1,
fn parse_flag_list_item(s: &str) -> IResult<&str, MqttParsedFlagItem> {
let (s, _) = opt(is_a(" "))(s)?;
let (s, neg) = opt(tag("!"))(s)?;
let neg = neg.is_some();
let (s, value) = alt((value(0x8, tag("dup")), value(0x1, tag("retain"))))(s)?;
let (s, _) = opt(is_a(" ,"))(s)?;
Ok((s, MqttParsedFlagItem { neg, value }))
}
fn parse_flag_list(s: &str) -> IResult<&str, Vec<MqttParsedFlagItem>> {
return many1(parse_flag_list_item)(s);
}
fn parse_flags(s: &str) -> Option<DetectUintData<u8>> {
// try first numerical value
if let Ok((_, ctx)) = detect_parse_uint::<u8>(s) {
return Some(ctx);
}
// otherwise, try strings for bitmask
if let Ok((rem, l)) = parse_flag_list(s) {
if !rem.is_empty() {
SCLogWarning!("junk at the end of mqtt.flags");
return None;
}
let mut arg1 = 0;
let mut arg2 = 0;
for elem in l.iter() {
if elem.value & arg1 != 0 {
SCLogWarning!("Repeated bitflag for mqtt.flags");
return None;
}
arg1 |= elem.value;
if !elem.neg {
arg2 |= elem.value;
}
}
let ctx = DetectUintData::<u8> {
arg1,
arg2,
mode: DetectUintMode::DetectUintModeBitmask,
};
return Some(ctx);
}
return None;
} }
unsafe extern "C" fn mqtt_parse_flags( unsafe extern "C" fn mqtt_parse_flags(
@ -729,7 +679,7 @@ unsafe extern "C" fn mqtt_parse_flags(
) -> *mut DetectUintData<u8> { ) -> *mut DetectUintData<u8> {
let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe
if let Ok(s) = ft_name.to_str() { if let Ok(s) = ft_name.to_str() {
if let Some(ctx) = parse_flags(s) { if let Some(ctx) = detect_parse_uint_bitflags::<u8, MqttFlag>(s) {
let boxed = Box::new(ctx); let boxed = Box::new(ctx);
return Box::into_raw(boxed) as *mut _; return Box::into_raw(boxed) as *mut _;
} }
@ -792,57 +742,15 @@ unsafe extern "C" fn mqtt_flags_free(_de: *mut DetectEngineCtx, ctx: *mut c_void
SCDetectU8Free(ctx); SCDetectU8Free(ctx);
} }
fn parse_conn_flag_list_item(s: &str) -> IResult<&str, MqttParsedFlagItem> { #[repr(u8)]
let (s, _) = opt(is_a(" "))(s)?; #[derive(EnumStringU8)]
let (s, neg) = opt(tag("!"))(s)?; #[allow(non_camel_case_types)]
let neg = neg.is_some(); pub enum MqttConnFlag {
let (s, value) = alt(( Username = 0x80,
value(0x80, tag("username")), Password = 0x40,
value(0x40, tag("password")), Will = 0x20,
// longer version first Will_retain = 0x4,
value(0x4, tag("will_retain")), Clean_session = 0x2,
value(0x20, tag("will")),
value(0x2, tag("clean_session")),
))(s)?;
let (s, _) = opt(is_a(" ,"))(s)?;
Ok((s, MqttParsedFlagItem { neg, value }))
}
fn parse_conn_flag_list(s: &str) -> IResult<&str, Vec<MqttParsedFlagItem>> {
return many1(parse_conn_flag_list_item)(s);
}
fn parse_conn_flags(s: &str) -> Option<DetectUintData<u8>> {
// try first numerical value
if let Ok((_, ctx)) = detect_parse_uint::<u8>(s) {
return Some(ctx);
}
// otherwise, try strings for bitmask
if let Ok((rem, l)) = parse_conn_flag_list(s) {
if !rem.is_empty() {
SCLogWarning!("junk at the end of mqtt.connect.flags");
return None;
}
let mut arg1 = 0;
let mut arg2 = 0;
for elem in l.iter() {
if elem.value & arg1 != 0 {
SCLogWarning!("Repeated bitflag for mqtt.connect.flags");
return None;
}
arg1 |= elem.value;
if !elem.neg {
arg2 |= elem.value;
}
}
let ctx = DetectUintData::<u8> {
arg1,
arg2,
mode: DetectUintMode::DetectUintModeBitmask,
};
return Some(ctx);
}
return None;
} }
unsafe extern "C" fn mqtt_parse_conn_flags( unsafe extern "C" fn mqtt_parse_conn_flags(
@ -850,7 +758,7 @@ unsafe extern "C" fn mqtt_parse_conn_flags(
) -> *mut DetectUintData<u8> { ) -> *mut DetectUintData<u8> {
let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe
if let Ok(s) = ft_name.to_str() { if let Ok(s) = ft_name.to_str() {
if let Some(ctx) = parse_conn_flags(s) { if let Some(ctx) = detect_parse_uint_bitflags::<u8, MqttConnFlag>(s) {
let boxed = Box::new(ctx); let boxed = Box::new(ctx);
return Box::into_raw(boxed) as *mut _; return Box::into_raw(boxed) as *mut _;
} }
@ -1283,49 +1191,60 @@ mod test {
#[test] #[test]
fn mqtt_parse_flags() { fn mqtt_parse_flags() {
let ctx = parse_flags("retain").unwrap(); let ctx = detect_parse_uint_bitflags::<u8, MqttFlag>("retain").unwrap();
assert_eq!(ctx.arg1, 1); assert_eq!(ctx.arg1, 1);
assert_eq!(ctx.arg2, 1); assert_eq!(ctx.arg2, 1);
let ctx = parse_flags("dup").unwrap(); let ctx = detect_parse_uint_bitflags::<u8, MqttFlag>("dup").unwrap();
assert_eq!(ctx.arg1, 8); assert_eq!(ctx.arg1, 8);
assert_eq!(ctx.arg2, 8); assert_eq!(ctx.arg2, 8);
let ctx = parse_flags("retain,dup").unwrap(); let ctx = detect_parse_uint_bitflags::<u8, MqttFlag>("retain,dup").unwrap();
assert_eq!(ctx.arg1, 8 | 1); assert_eq!(ctx.arg1, 8 | 1);
assert_eq!(ctx.arg2, 8 | 1); assert_eq!(ctx.arg2, 8 | 1);
let ctx = parse_flags("dup, retain").unwrap(); let ctx = detect_parse_uint_bitflags::<u8, MqttFlag>("dup, retain").unwrap();
assert_eq!(ctx.arg1, 8 | 1); assert_eq!(ctx.arg1, 8 | 1);
assert_eq!(ctx.arg2, 8 | 1); assert_eq!(ctx.arg2, 8 | 1);
let ctx = parse_flags("retain,!dup").unwrap(); let ctx = detect_parse_uint_bitflags::<u8, MqttFlag>("retain,!dup").unwrap();
assert_eq!(ctx.arg1, 1 | 8); assert_eq!(ctx.arg1, 1 | 8);
assert_eq!(ctx.arg2, 1); assert_eq!(ctx.arg2, 1);
assert!(parse_flags("ref").is_none()); assert!(detect_parse_uint_bitflags::<u8, MqttFlag>("ref").is_none());
assert!(parse_flags("dup,!").is_none()); assert!(detect_parse_uint_bitflags::<u8, MqttFlag>("dup,!").is_none());
assert!(parse_flags("dup,!dup").is_none()); assert!(detect_parse_uint_bitflags::<u8, MqttFlag>("dup,!dup").is_none());
assert!(parse_flags("!retain,retain").is_none()); assert!(detect_parse_uint_bitflags::<u8, MqttFlag>("!retain,retain").is_none());
} }
#[test] #[test]
fn mqtt_parse_conn_flags() { fn mqtt_parse_conn_flags() {
let ctx = parse_conn_flags("username").unwrap(); let ctx = detect_parse_uint_bitflags::<u8, MqttConnFlag>("username").unwrap();
assert_eq!(ctx.arg1, 0x80); assert_eq!(ctx.arg1, 0x80);
assert_eq!(ctx.arg2, 0x80); assert_eq!(ctx.arg2, 0x80);
let ctx = parse_conn_flags("username,password,will,will_retain,clean_session").unwrap(); let ctx = detect_parse_uint_bitflags::<u8, MqttConnFlag>(
"username,password,will,will_retain,clean_session",
)
.unwrap();
assert_eq!(ctx.arg1, 0xE6); assert_eq!(ctx.arg1, 0xE6);
assert_eq!(ctx.arg2, 0xE6); assert_eq!(ctx.arg2, 0xE6);
let ctx = let ctx = detect_parse_uint_bitflags::<u8, MqttConnFlag>(
parse_conn_flags("!username,!password,!will,!will_retain,!clean_session").unwrap(); "!username,!password,!will,!will_retain,!clean_session",
)
.unwrap();
assert_eq!(ctx.arg1, 0xE6); assert_eq!(ctx.arg1, 0xE6);
assert_eq!(ctx.arg2, 0); assert_eq!(ctx.arg2, 0);
let ctx = parse_conn_flags(" username,password").unwrap(); let ctx = detect_parse_uint_bitflags::<u8, MqttConnFlag>(" username,password").unwrap();
assert_eq!(ctx.arg1, 0xC0); assert_eq!(ctx.arg1, 0xC0);
assert_eq!(ctx.arg2, 0xC0); assert_eq!(ctx.arg2, 0xC0);
assert!(parse_conn_flags("foobar").is_none()); assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>("foobar").is_none());
assert!(parse_conn_flags("will,!").is_none()); assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>("will,!").is_none());
assert!(parse_conn_flags("").is_none()); assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>("").is_none());
assert!(parse_conn_flags("username, username").is_none()); assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>("username, username").is_none());
assert!(parse_conn_flags("!username, username").is_none()); assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>("!username, username").is_none());
assert!(parse_conn_flags("!username,password,!password").is_none()); assert!(
assert!(parse_conn_flags("will, username,password, !will, will").is_none()); detect_parse_uint_bitflags::<u8, MqttConnFlag>("!username,password,!password")
.is_none()
);
assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>(
"will, username,password, !will, will"
)
.is_none());
} }
#[test] #[test]

Loading…
Cancel
Save