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::detect::uint::{
detect_match_uint, detect_parse_array_uint_enum, detect_parse_uint, detect_uint_match_at_index,
DetectUintArrayData, DetectUintData, DetectUintMode, SCDetectU8Free, SCDetectU8Parse,
detect_match_uint, detect_parse_array_uint_enum, detect_parse_uint_bitflags,
detect_uint_match_at_index, DetectUintArrayData, DetectUintData, SCDetectU8Free,
SCDetectU8Parse,
};
use crate::detect::{
helper_keyword_register_multi_buffer, helper_keyword_register_sticky_buffer,
@ -34,12 +35,6 @@ use suricata_sys::sys::{
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 crate::conf::conf_get;
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);
}
// maybe to factor with websocket.flags
struct MqttParsedFlagItem {
neg: bool,
value: u8,
}
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;
#[repr(u8)]
#[derive(EnumStringU8)]
pub enum MqttFlag {
Dup = 0x8,
Retain = 0x1,
}
unsafe extern "C" fn mqtt_parse_flags(
@ -729,7 +679,7 @@ unsafe extern "C" fn mqtt_parse_flags(
) -> *mut DetectUintData<u8> {
let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe
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);
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);
}
fn parse_conn_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(0x80, tag("username")),
value(0x40, tag("password")),
// longer version first
value(0x4, tag("will_retain")),
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;
#[repr(u8)]
#[derive(EnumStringU8)]
#[allow(non_camel_case_types)]
pub enum MqttConnFlag {
Username = 0x80,
Password = 0x40,
Will = 0x20,
Will_retain = 0x4,
Clean_session = 0x2,
}
unsafe extern "C" fn mqtt_parse_conn_flags(
@ -850,7 +758,7 @@ unsafe extern "C" fn mqtt_parse_conn_flags(
) -> *mut DetectUintData<u8> {
let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe
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);
return Box::into_raw(boxed) as *mut _;
}
@ -1283,49 +1191,60 @@ mod test {
#[test]
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.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.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.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.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.arg2, 1);
assert!(parse_flags("ref").is_none());
assert!(parse_flags("dup,!").is_none());
assert!(parse_flags("dup,!dup").is_none());
assert!(parse_flags("!retain,retain").is_none());
assert!(detect_parse_uint_bitflags::<u8, MqttFlag>("ref").is_none());
assert!(detect_parse_uint_bitflags::<u8, MqttFlag>("dup,!").is_none());
assert!(detect_parse_uint_bitflags::<u8, MqttFlag>("dup,!dup").is_none());
assert!(detect_parse_uint_bitflags::<u8, MqttFlag>("!retain,retain").is_none());
}
#[test]
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.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.arg2, 0xE6);
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.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.arg2, 0xC0);
assert!(parse_conn_flags("foobar").is_none());
assert!(parse_conn_flags("will,!").is_none());
assert!(parse_conn_flags("").is_none());
assert!(parse_conn_flags("username, username").is_none());
assert!(parse_conn_flags("!username, username").is_none());
assert!(parse_conn_flags("!username,password,!password").is_none());
assert!(parse_conn_flags("will, username,password, !will, will").is_none());
assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>("foobar").is_none());
assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>("will,!").is_none());
assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>("").is_none());
assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>("username, username").is_none());
assert!(detect_parse_uint_bitflags::<u8, MqttConnFlag>("!username, username").is_none());
assert!(
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]

Loading…
Cancel
Save