From be60031175038f78310a34dbf2683f1ce4fd0c85 Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Wed, 8 Jun 2022 08:03:44 -0400 Subject: [PATCH] detect/bytemath: Convert parser to Rust Issue: 5077 This commit - Converts the PCRE based parser to Rust. - Adds unit tests to the new Rust modules - Removes the PCRE parser from detect-bytemath.c - Adjusts the C source modules to refer to the Rust definitions --- rust/src/detect_parser/byte_math.rs | 1007 ++++++++++++++++++++++++ rust/src/detect_parser/error.rs | 37 + rust/src/detect_parser/mod.rs | 20 + rust/src/detect_parser/parser.rs | 38 + rust/src/lib.rs | 1 + src/detect-byte.c | 2 + src/detect-bytemath.c | 390 +-------- src/detect-bytemath.h | 48 -- src/detect-engine-content-inspection.c | 9 +- 9 files changed, 1127 insertions(+), 425 deletions(-) create mode 100644 rust/src/detect_parser/byte_math.rs create mode 100644 rust/src/detect_parser/error.rs create mode 100644 rust/src/detect_parser/mod.rs create mode 100644 rust/src/detect_parser/parser.rs diff --git a/rust/src/detect_parser/byte_math.rs b/rust/src/detect_parser/byte_math.rs new file mode 100644 index 00000000000..acbe9d9c16c --- /dev/null +++ b/rust/src/detect_parser/byte_math.rs @@ -0,0 +1,1007 @@ +/* Copyright (C) 2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// Author: Jeff Lucovsky + +use crate::detect_parser::error::RuleParseError; +use crate::detect_parser::parser::{parse_token, take_until_whitespace}; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +use nom7::bytes::complete::tag; +use nom7::character::complete::multispace0; +use nom7::sequence::preceded; +use nom7::{Err, IResult}; +use std::str; + +pub const DETECT_BYTEMATH_FLAG_RELATIVE: u32 = 0x01; +pub const DETECT_BYTEMATH_FLAG_STRING: u32 = 0x02; +pub const DETECT_BYTEMATH_FLAG_BITMASK: u32 = 0x04; +pub const DETECT_BYTEMATH_FLAG_ENDIAN: u32 = 0x08; +pub const DETECT_BYTEMATH_FLAG_RVALUE_VAR: u32 = 0x10; + +// operator: +, -, /, *, <<, >> +pub const DETECT_BYTEMATH_OPERATOR_NONE: u8 = 1; +pub const DETECT_BYTEMATH_OPERATOR_PLUS: u8 = 2; +pub const DETECT_BYTEMATH_OPERATOR_MINUS: u8 = 3; +pub const DETECT_BYTEMATH_OPERATOR_DIVIDE: u8 = 4; +pub const DETECT_BYTEMATH_OPERATOR_MULTIPLY: u8 = 5; +pub const DETECT_BYTEMATH_OPERATOR_LSHIFT: u8 = 6; +pub const DETECT_BYTEMATH_OPERATOR_RSHIFT: u8 = 7; + +// endian +pub const DETECT_BYTEMATH_ENDIAN_NONE: u8 = 0; +pub const DETECT_BYTEMATH_ENDIAN_BIG: u8 = 1; +pub const DETECT_BYTEMATH_ENDIAN_LITTLE: u8 = 2; +pub const DETECT_BYTEMATH_ENDIAN_DCE: u8 = 3; +pub const DETECT_BYTEMATH_ENDIAN_DEFAULT: u8 = DETECT_BYTEMATH_ENDIAN_BIG; + +pub const DETECT_BYTEMATH_BASE_HEX: u8 = 16; +pub const DETECT_BYTEMATH_BASE_DEC: u8 = 10; +pub const DETECT_BYTEMATH_BASE_OCT: u8 = 8; +pub const DETECT_BYTEMATH_BASE_NONE: u8 = 0; +pub const DETECT_BYTEMATH_BASE_DEFAULT: u8 = DETECT_BYTEMATH_BASE_DEC; + +// Fixed position parameter count: bytes, offset, oper, rvalue, result +// result is not parsed with the fixed position parameters as it's +// often swapped with optional parameters +pub const DETECT_BYTEMATH_FIXED_PARAM_COUNT: u8 = 5; +// Optional parameters: endian, relative, string, dce, bitmask +pub const DETECT_BYTEMATH_MAX_PARAM_COUNT: u8 = 10; + +#[derive(Debug)] +enum ResultValue { + Numeric(u64), + String(String), +} + +#[derive(Debug)] +#[repr(C)] +pub struct DetectByteMathData { + rvalue_str: *const c_char, + result: *const c_char, + rvalue: u32, + flags: u32, + offset: i32, + bitmask_val: u32, + bitmask_shift_count: u16, + id: u16, + local_id: u8, + nbytes: u8, + oper: u8, + endian: u8, // big, little, dce + base: u8, // From string or dce +} + +impl Default for DetectByteMathData { + fn default() -> Self { + DetectByteMathData { + local_id: 0, + flags: 0, + nbytes: 0, + offset: 0, + oper: 0, + rvalue_str: std::ptr::null_mut(), + rvalue: 0, + result: std::ptr::null_mut(), + endian: DETECT_BYTEMATH_ENDIAN_DEFAULT, + base: DETECT_BYTEMATH_BASE_DEFAULT, + bitmask_val: 0, + bitmask_shift_count: 0, + id: 0, + } + } +} + +impl DetectByteMathData { + pub fn new() -> Self { + Self { + ..Default::default() + } + } +} + +fn get_string_value(value: &str) -> Result { + let res = match value { + "hex" => DETECT_BYTEMATH_BASE_HEX, + "oct" => DETECT_BYTEMATH_BASE_OCT, + "dec" => DETECT_BYTEMATH_BASE_DEC, + _ => return Err(()), + }; + + Ok(res) +} + +fn get_oper_value(value: &str) -> Result { + let res = match value { + "+" => DETECT_BYTEMATH_OPERATOR_PLUS, + "-" => DETECT_BYTEMATH_OPERATOR_MINUS, + "/" => DETECT_BYTEMATH_OPERATOR_DIVIDE, + "*" => DETECT_BYTEMATH_OPERATOR_MULTIPLY, + "<<" => DETECT_BYTEMATH_OPERATOR_LSHIFT, + ">>" => DETECT_BYTEMATH_OPERATOR_RSHIFT, + _ => return Err(()), + }; + + Ok(res) +} + +fn get_endian_value(value: &str) -> Result { + let res = match value { + "big" => 1, + "little" => 2, + _ => return Err(()), + }; + + Ok(res) +} + +// Parsed as a u64 for validation with u32 {min,max} +fn parse_rvalue(input: &str) -> IResult<&str, ResultValue, RuleParseError<&str>> { + let (input, rvalue) = parse_token(input)?; + if let Ok(val) = rvalue.parse::() { + Ok((input, ResultValue::Numeric(val))) + } else { + Ok((input, ResultValue::String(rvalue.to_string()))) + } +} + +fn parse_bytemath(input: &str) -> IResult<&str, DetectByteMathData, RuleParseError<&str>> { + // Inner utility function for easy error creation. + fn make_error(reason: String) -> nom7::Err> { + Err::Error(RuleParseError::InvalidByteMath(reason)) + } + let (_, values) = nom7::multi::separated_list1( + tag(","), + preceded(multispace0, nom7::bytes::complete::is_not(",")), + )(input)?; + + if values.len() < DETECT_BYTEMATH_FIXED_PARAM_COUNT as usize + || values.len() > DETECT_BYTEMATH_MAX_PARAM_COUNT as usize + { + return Err(make_error(format!("Incorrect argument string; at least {} values must be specified but no more than {}: {:?}", + DETECT_BYTEMATH_FIXED_PARAM_COUNT, DETECT_BYTEMATH_MAX_PARAM_COUNT, input))); + } + + let mut byte_math = DetectByteMathData::new(); + for value in &values[0..] { + let (val, name) = take_until_whitespace(value)?; + match name.trim() { + "oper" => { + byte_math.oper = match get_oper_value(val.trim()) { + Ok(val) => val, + Err(_) => { + return Err(make_error(format!("unknown oper value {}", val))); + } + }; + } + "result" => { + let tmp: String = val + .trim() + .parse() + .map_err(|_| make_error(format!("invalid result: {}", val)))?; + byte_math.result = CString::new(tmp).unwrap().into_raw(); + } + "rvalue" => { + let (_, res) = match parse_rvalue(val.trim()) { + Ok(val) => val, + Err(_) => { + return Err(make_error(format!("invalid rvalue value: {}", val))); + } + }; + match res { + ResultValue::Numeric(val) => { + if val > u32::MIN.into() && val <= u32::MAX.into() { + byte_math.rvalue = val as u32 + } else { + return Err(make_error(format!( + "invalid rvalue value: must be between {} and {}: {}", + 1, + u32::MAX, + val + ))); + } + } + ResultValue::String(val) => { + byte_math.rvalue_str = CString::new(val).unwrap().into_raw(); + byte_math.flags |= DETECT_BYTEMATH_FLAG_RVALUE_VAR; + } + } + } + "endian" => { + if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_ENDIAN) { + return Err(make_error("endianess already set".to_string())); + } + byte_math.endian = match get_endian_value(val.trim()) { + Ok(val) => val, + Err(_) => { + return Err(make_error(format!("invalid endian value: {}", val))); + } + }; + byte_math.flags |= DETECT_BYTEMATH_FLAG_ENDIAN; + } + "string" => { + byte_math.base = match get_string_value(val.trim()) { + Ok(val) => val, + Err(_) => { + return Err(make_error(format!("invalid string value: {}", val))); + } + }; + byte_math.flags |= DETECT_BYTEMATH_FLAG_STRING; + } + "relative" => { + byte_math.flags |= DETECT_BYTEMATH_FLAG_RELATIVE; + } + "dce" => { + if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_ENDIAN) { + return Err(make_error("endianess already set".to_string())); + } + byte_math.flags |= DETECT_BYTEMATH_FLAG_ENDIAN; + byte_math.endian = DETECT_BYTEMATH_ENDIAN_DCE; + } + "bitmask" => { + let val = val.trim(); + let trimmed = if val.starts_with("0x") || val.starts_with("0X") { + &val[2..] + } else { + val + }; + + let val = u32::from_str_radix(trimmed, 16) + .map_err(|_| make_error(format!("invalid bitmask value: {}", value)))?; + byte_math.bitmask_val = val; + byte_math.flags |= DETECT_BYTEMATH_FLAG_BITMASK; + } + "offset" => { + byte_math.offset = val + .trim() + .parse::() + .map_err(|_| make_error(format!("invalid offset value: {}", val)))?; + if byte_math.offset > 65535 || byte_math.offset < -65535 { + return Err(make_error(format!( + "invalid offset value: must be between -65535 and 65535: {}", + val + ))); + } + } + "bytes" => { + byte_math.nbytes = val + .trim() + .parse() + .map_err(|_| make_error(format!("invalid bytes value: {}", val)))?; + if byte_math.nbytes < 1 || byte_math.nbytes > 10 { + return Err(make_error(format!( + "invalid bytes value: must be between 1 and 10: {}", + byte_math.nbytes + ))); + } + } + _ => { + return Err(make_error(format!("unknown byte_math keyword: {}", name))); + } + }; + } + + match byte_math.oper { + DETECT_BYTEMATH_OPERATOR_LSHIFT | DETECT_BYTEMATH_OPERATOR_RSHIFT => { + if byte_math.nbytes > 4 { + return Err(make_error(format!("nbytes must be 1 through 4 (inclusive) when used with \"<<\" or \">>\"; {} is not valid", byte_math.nbytes))); + } + } + _ => {} + }; + Ok((input, byte_math)) +} + +/// Intermediary function between the C code and the parsing functions. +#[no_mangle] +pub unsafe extern "C" fn rs_bytemath_parse(c_arg: *const c_char) -> *mut DetectByteMathData { + if c_arg.is_null() { + return std::ptr::null_mut(); + } + + let arg = match CStr::from_ptr(c_arg).to_str() { + Ok(arg) => arg, + _ => { + return std::ptr::null_mut(); + } + }; + match parse_bytemath(arg) { + Ok(detect) => return Box::into_raw(Box::new(detect.1)) as *mut DetectByteMathData, + Err(_) => return std::ptr::null_mut(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn rs_bytemath_free(ptr: *mut DetectByteMathData) { + if !ptr.is_null() { + let bmd = Box::from_raw(ptr as *mut DetectByteMathData); + if !bmd.result.is_null() { + let _ = Box::from_raw(bmd.result as *mut *const c_char); + } + if !bmd.rvalue_str.is_null() { + let _ = Box::from_raw(bmd.rvalue_str as *mut *const c_char); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + // structure equality only used by test cases + impl PartialEq for DetectByteMathData { + fn eq(&self, other: &Self) -> bool { + let mut res: bool = false; + + if !self.rvalue_str.is_null() && !other.rvalue_str.is_null() { + let s_val = unsafe { CStr::from_ptr(self.rvalue_str) }; + let o_val = unsafe { CStr::from_ptr(other.rvalue_str) }; + res = s_val == o_val; + } else if !self.rvalue_str.is_null() || !other.rvalue_str.is_null() { + return false; + } + + if !self.result.is_null() && !self.result.is_null() { + let s_val = unsafe { CStr::from_ptr(self.result) }; + let o_val = unsafe { CStr::from_ptr(other.result) }; + res = s_val == o_val; + } else if !self.result.is_null() || !self.result.is_null() { + return false; + } + + !res || self.local_id == other.local_id + && self.nbytes == other.nbytes + && self.oper == other.oper + && self.rvalue == other.rvalue + && self.flags == other.flags + && self.endian == other.endian + && self.base == other.base + && self.bitmask_val == other.bitmask_val + && self.bitmask_shift_count == other.bitmask_shift_count + && self.id == other.id + } + } + + fn valid_test( + args: &str, nbytes: u8, offset: i32, oper: u8, rvalue_str: &str, rvalue: u32, result: &str, + base: u8, endian: u8, bitmask_val: u32, flags: u32, + ) { + let bmd = DetectByteMathData { + nbytes: nbytes, + offset: offset, + oper: oper, + rvalue_str: if rvalue_str != "" { + CString::new(rvalue_str).unwrap().into_raw() + } else { + std::ptr::null_mut() + }, + rvalue: rvalue, + result: CString::new(result).unwrap().into_raw(), + base: base, + endian: endian, + bitmask_val: bitmask_val, + flags: flags, + ..Default::default() + }; + + match parse_bytemath(args) { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + } + + #[test] + fn test_parser_valid() { + valid_test( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result myresult, dce, string dec", + 4, + 3933, + DETECT_BYTEMATH_OPERATOR_PLUS, + "myrvalue", + 0, + "myresult", + DETECT_BYTEMATH_BASE_DEC, + DETECT_BYTEMATH_ENDIAN_DCE, + 0, + DETECT_BYTEMATH_FLAG_RVALUE_VAR + | DETECT_BYTEMATH_FLAG_STRING + | DETECT_BYTEMATH_FLAG_ENDIAN, + ); + + valid_test( + "bytes 4, offset 3933, oper +, rvalue 99, result other, dce, string dec", + 4, + 3933, + DETECT_BYTEMATH_OPERATOR_PLUS, + "", + 99, + "other", + DETECT_BYTEMATH_BASE_DEC, + DETECT_BYTEMATH_ENDIAN_DCE, + 0, + DETECT_BYTEMATH_FLAG_STRING | DETECT_BYTEMATH_FLAG_ENDIAN, + ); + + valid_test( + "bytes 4, offset -3933, oper +, rvalue myrvalue, result foo", + 4, + -3933, + DETECT_BYTEMATH_OPERATOR_PLUS, + "rvalue", + 0, + "foo", + DETECT_BYTEMATH_BASE_DEFAULT, + DETECT_BYTEMATH_ENDIAN_DEFAULT, + 0, + DETECT_BYTEMATH_FLAG_RVALUE_VAR, + ); + + // Out of order + valid_test( + "string dec, endian big, result other, rvalue 99, oper +, offset 3933, bytes 4", + 4, + 3933, + DETECT_BYTEMATH_OPERATOR_PLUS, + "", + 99, + "other", + DETECT_BYTEMATH_BASE_DEC, + DETECT_BYTEMATH_ENDIAN_BIG, + 0, + DETECT_BYTEMATH_FLAG_STRING | DETECT_BYTEMATH_FLAG_ENDIAN, + ); + } + + #[test] + fn test_parser_string_valid() { + let mut bmd = DetectByteMathData { + nbytes: 4, + offset: 3933, + oper: DETECT_BYTEMATH_OPERATOR_PLUS, + rvalue_str: CString::new("myrvalue").unwrap().into_raw(), + rvalue: 0, + result: CString::new("foo").unwrap().into_raw(), + endian: DETECT_BYTEMATH_ENDIAN_DEFAULT, + base: DETECT_BYTEMATH_BASE_DEC, + flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_STRING, + ..Default::default() + }; + + match parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string dec", + ) { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR; + bmd.base = DETECT_BYTEMATH_BASE_DEFAULT; + match parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_STRING; + bmd.base = DETECT_BYTEMATH_BASE_HEX; + match parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string hex", + ) { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.base = DETECT_BYTEMATH_BASE_OCT; + match parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string oct", + ) { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + } + + #[test] + fn test_parser_string_invalid() { + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string decimal" + ) + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string hexadecimal" + ) + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string octal" + ) + .is_err() + ); + } + + #[test] + // bytes must be between 1 and 10; when combined with rshift/lshift, must be 4 or less + fn test_parser_bytes_invalid() { + assert_eq!( + true, + parse_bytemath("bytes 0, offset 3933, oper +, rvalue myrvalue, result foo").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 11, offset 3933, oper +, rvalue myrvalue, result foo").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 5, offset 3933, oper >>, rvalue myrvalue, result foo").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 5, offset 3933, oper <<, rvalue myrvalue, result foo").is_err() + ); + } + + #[test] + fn test_parser_bitmask_invalid() { + assert_eq!( + true, + parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x") + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask x12345678" + ) + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask X12345678" + ) + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x123456789012" + ) + .is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0q") + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask maple" + ) + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0xGHIJKLMN" + ) + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask #*#*@-" + ) + .is_err() + ); + } + + #[test] + fn test_parser_bitmask_valid() { + let mut bmd = DetectByteMathData { + nbytes: 4, + offset: 3933, + oper: DETECT_BYTEMATH_OPERATOR_PLUS, + rvalue_str: CString::new("myrvalue").unwrap().into_raw(), + rvalue: 0, + result: CString::new("foo").unwrap().into_raw(), + endian: DETECT_BYTEMATH_ENDIAN_BIG, + base: DETECT_BYTEMATH_BASE_DEFAULT, + flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_BITMASK, + ..Default::default() + }; + + bmd.bitmask_val = 0x12345678; + match parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x12345678", + ) { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.bitmask_val = 0xffff1234; + match parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask ffff1234", + ) { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.bitmask_val = 0xffff1234; + match parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0Xffff1234", + ) { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + } + #[test] + fn test_parser_endian_valid() { + let mut bmd = DetectByteMathData { + nbytes: 4, + offset: 3933, + oper: DETECT_BYTEMATH_OPERATOR_PLUS, + rvalue_str: CString::new("myrvalue").unwrap().into_raw(), + rvalue: 0, + result: CString::new("foo").unwrap().into_raw(), + endian: DETECT_BYTEMATH_ENDIAN_BIG, + base: DETECT_BYTEMATH_BASE_DEFAULT, + flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_ENDIAN, + ..Default::default() + }; + + match parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian big", + ) { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.endian = DETECT_BYTEMATH_ENDIAN_LITTLE; + match parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian little", + ) { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.endian = DETECT_BYTEMATH_ENDIAN_DCE; + match parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, dce") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.endian = DETECT_BYTEMATH_ENDIAN_DEFAULT; + bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR; + match parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + } + + #[test] + fn test_parser_endian_invalid() { + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian bigger" + ) + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian smaller" + ) + .is_err() + ); + + // endianess can only be specified once + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian big, dce" + ) + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian small, endian big" + ) + .is_err() + ); + assert_eq!( + true, + parse_bytemath( + "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian small, dce" + ) + .is_err() + ); + } + + #[test] + fn test_parser_oper_valid() { + let mut bmd = DetectByteMathData { + nbytes: 4, + offset: 3933, + oper: DETECT_BYTEMATH_OPERATOR_PLUS, + rvalue_str: CString::new("myrvalue").unwrap().into_raw(), + rvalue: 0, + result: CString::new("foo").unwrap().into_raw(), + endian: DETECT_BYTEMATH_ENDIAN_BIG, + base: DETECT_BYTEMATH_BASE_DEFAULT, + flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR, + ..Default::default() + }; + + match parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.oper = DETECT_BYTEMATH_OPERATOR_MINUS; + match parse_bytemath("bytes 4, offset 3933, oper -, rvalue myrvalue, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.oper = DETECT_BYTEMATH_OPERATOR_MULTIPLY; + match parse_bytemath("bytes 4, offset 3933, oper *, rvalue myrvalue, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + bmd.oper = DETECT_BYTEMATH_OPERATOR_DIVIDE; + match parse_bytemath("bytes 4, offset 3933, oper /, rvalue myrvalue, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + bmd.oper = DETECT_BYTEMATH_OPERATOR_RSHIFT; + match parse_bytemath("bytes 4, offset 3933, oper >>, rvalue myrvalue, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + bmd.oper = DETECT_BYTEMATH_OPERATOR_LSHIFT; + match parse_bytemath("bytes 4, offset 3933, oper <<, rvalue myrvalue, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + } + + #[test] + fn test_parser_oper_invalid() { + assert_eq!( + true, + parse_bytemath("bytes 4, offset 0, oper !, rvalue myvalue, result foo").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 4, offset 0, oper ^, rvalue myvalue, result foo").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 4, offset 0, oper <>, rvalue myvalue, result foo").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 4, offset 0, oper ><, rvalue myvalue, result foo").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 4, offset 0, oper <, rvalue myvalue, result foo").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 4, offset 0, oper >, rvalue myvalue, result foo").is_err() + ); + } + + #[test] + fn test_parser_rvalue_valid() { + let mut bmd = DetectByteMathData { + nbytes: 4, + offset: 47303, + oper: DETECT_BYTEMATH_OPERATOR_MULTIPLY, + rvalue_str: std::ptr::null_mut(), + rvalue: 4294967295, + result: CString::new("foo").unwrap().into_raw(), + endian: DETECT_BYTEMATH_ENDIAN_DEFAULT, + base: DETECT_BYTEMATH_BASE_DEFAULT, + ..Default::default() + }; + + match parse_bytemath("bytes 4, offset 47303, oper *, rvalue 4294967295, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.rvalue = 1; + match parse_bytemath("bytes 4, offset 47303, oper *, rvalue 1, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + } + + #[test] + fn test_parser_rvalue_invalid() { + assert_eq!( + true, + parse_bytemath("bytes 4, offset 47303, oper *, rvalue 4294967296, result foo").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 4, offset 47303, oper +, rvalue 0, result foo").is_err() + ); + } + + #[test] + fn test_parser_offset_valid() { + let mut bmd = DetectByteMathData { + nbytes: 4, + offset: -65535, + oper: DETECT_BYTEMATH_OPERATOR_MULTIPLY, + rvalue_str: CString::new("myrvalue").unwrap().into_raw(), + rvalue: 0, + result: CString::new("foo").unwrap().into_raw(), + endian: DETECT_BYTEMATH_ENDIAN_DEFAULT, + base: DETECT_BYTEMATH_BASE_DEFAULT, + flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR, + ..Default::default() + }; + + match parse_bytemath("bytes 4, offset -65535, oper *, rvalue myrvalue, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + + bmd.offset = 65535; + match parse_bytemath("bytes 4, offset 65535, oper *, rvalue myrvalue, result foo") { + Ok((_, val)) => { + assert_eq!(val, bmd); + } + Err(_) => { + assert!(false); + } + } + } + + #[test] + // offset: numeric values must be between -65535 and 65535 + fn test_parser_offset_invalid() { + assert_eq!( + true, + parse_bytemath("bytes 4, offset -70000, oper *, rvalue myvalue, result foo").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 4, offset 70000, oper +, rvalue myvalue, result foo").is_err() + ); + } + + #[test] + fn test_parser_incomplete_args() { + assert_eq!(true, parse_bytemath("").is_err()); + assert_eq!(true, parse_bytemath("bytes 4").is_err()); + assert_eq!(true, parse_bytemath("bytes 4, offset 0").is_err()); + assert_eq!(true, parse_bytemath("bytes 4, offset 0, oper <<").is_err()); + } + + #[test] + fn test_parser_invalid_args() { + assert_eq!(true, parse_bytemath("monkey banana").is_err()); + assert_eq!(true, parse_bytemath("bytes nan").is_err()); + assert_eq!(true, parse_bytemath("bytes 4, offset nan").is_err()); + assert_eq!(true, parse_bytemath("bytes 4, offset 0, three 3, four 4, five 5, six 6, seven 7, eight 8, nine 9, ten 10, eleven 11").is_err()); + assert_eq!( + true, + parse_bytemath("bytes 4, offset 0, oper ><, rvalue myrvalue").is_err() + ); + assert_eq!( + true, + parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, endian endian").is_err() + ); + } +} diff --git a/rust/src/detect_parser/error.rs b/rust/src/detect_parser/error.rs new file mode 100644 index 00000000000..d0738c68744 --- /dev/null +++ b/rust/src/detect_parser/error.rs @@ -0,0 +1,37 @@ +/* Copyright (C) 2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use nom7::error::{ErrorKind, ParseError}; + +/// Custom rule parse errors. +/// +/// Implemented based on the Nom example for implementing custom errors. +#[derive(Debug, PartialEq)] +pub enum RuleParseError { + InvalidByteMath(String), + + Nom(I, ErrorKind), +} +impl ParseError for RuleParseError { + fn from_error_kind(input: I, kind: ErrorKind) -> Self { + RuleParseError::Nom(input, kind) + } + + fn append(_: I, _: ErrorKind, other: Self) -> Self { + other + } +} diff --git a/rust/src/detect_parser/mod.rs b/rust/src/detect_parser/mod.rs new file mode 100644 index 00000000000..308e01d1daf --- /dev/null +++ b/rust/src/detect_parser/mod.rs @@ -0,0 +1,20 @@ +/* Copyright (C) 2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +pub mod byte_math; +pub mod error; +pub mod parser; diff --git a/rust/src/detect_parser/parser.rs b/rust/src/detect_parser/parser.rs new file mode 100644 index 00000000000..eccf8fa8211 --- /dev/null +++ b/rust/src/detect_parser/parser.rs @@ -0,0 +1,38 @@ +/* Copyright (C) 2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use crate::detect_parser::error::RuleParseError; + +use nom7::bytes::complete::is_not; +use nom7::character::complete::multispace0; +use nom7::sequence::preceded; +use nom7::IResult; + +static WHITESPACE: &str = " \t\r\n"; +/// Parse all characters up until the next whitespace character. +pub fn take_until_whitespace(input: &str) -> IResult<&str, &str, RuleParseError<&str>> { + nom7::bytes::complete::is_not(WHITESPACE)(input) +} + +/// Parse the next token ignoring leading whitespace. +/// +/// A token is the next sequence of chars until a terminating character. Leading whitespace +/// is ignore. +pub fn parse_token(input: &str) -> IResult<&str, &str, RuleParseError<&str>> { + let terminators = "\n\r\t,;: "; + preceded(multispace0, is_not(terminators))(input) +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a8b729bf610..d0ea2af89bb 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -113,6 +113,7 @@ pub mod smb; pub mod krb; pub mod dcerpc; pub mod modbus; +pub mod detect_parser; pub mod ike; pub mod snmp; diff --git a/src/detect-byte.c b/src/detect-byte.c index a1560aac238..b27599fc9a2 100644 --- a/src/detect-byte.c +++ b/src/detect-byte.c @@ -25,6 +25,8 @@ #include "detect-byte.h" #include "detect-byte-extract.h" #include "detect-bytemath.h" +#include "detect-engine.h" +#include "rust-bindings.h" /** * \brief Used to retrieve args from BM. diff --git a/src/detect-bytemath.c b/src/detect-bytemath.c index 316a4e0413c..0c2063ec968 100644 --- a/src/detect-bytemath.c +++ b/src/detect-bytemath.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Open Information Security Foundation +/* Copyright (C) 2020-2022 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -28,6 +28,8 @@ #include "suricata-common.h" #include "threads.h" #include "decode.h" +#include "app-layer-parser.h" +#include "rust-bindings.h" #include "detect.h" #include "detect-parse.h" @@ -38,7 +40,6 @@ #include "detect-pcre.h" #include "detect-byte.h" #include "detect-bytemath.h" -#include "detect-isdataat.h" #include "app-layer-protos.h" @@ -52,67 +53,6 @@ #include "util-unittest-helper.h" #include "util-spm.h" -/* the default value of endianess to be used, if none's specified */ -#define DETECT_BYTEMATH_ENDIAN_DEFAULT DETECT_BYTEMATH_ENDIAN_BIG - -/* the base to be used if string mode is specified. These options would be - * specified in DetectByteMathData->base */ -#define DETECT_BYTEMATH_BASE_NONE 0 -#define DETECT_BYTEMATH_BASE_OCT 8 -#define DETECT_BYTEMATH_BASE_DEC 10 -#define DETECT_BYTEMATH_BASE_HEX 16 -#define DETECT_BYTEMATH_BASE_DEFAULT DETECT_BYTEMATH_BASE_DEC - -/* the max no of bytes that can be extracted in string mode - (string, hex) - * (string, oct) or (string, dec) */ -#define STRING_MAX_BYTES_TO_EXTRACT_FOR_OCT 23 -#define STRING_MAX_BYTES_TO_EXTRACT_FOR_DEC 20 -#define STRING_MAX_BYTES_TO_EXTRACT_FOR_HEX 14 - -/* the max no of bytes that can be extracted in non-string mode */ -#define NO_STRING_MAX_BYTES_TO_EXTRACT 4 - -#define PARSE_REGEX \ - "^" \ - "\\s*(bytes)\\s*(\\d+)\\s*" \ - ",\\s*(offset)\\s*([-]?\\d+)\\s*" \ - ",\\s*(oper)\\s*([-+\\/]{1}|<<|>>)\\s*" \ - ",\\s*(rvalue)\\s*(\\w+)\\s*" \ - ",\\s*(result)\\s*(\\w+)\\s*" \ - "(?:,\\s*(relative)\\s*)?" \ - "(?:,\\s*(endian)\\s*(big|little)\\s*)?" \ - "(?:,\\s*(string)\\s*(hex|dec|oct)\\s*)?" \ - "(?:,\\s*(dce)\\s*)?" \ - "(?:,\\s*(bitmask)\\s*(0?[xX]?[0-9a-fA-F]{2,8})\\s*)?" \ - "$" - -/* Mandatory value group numbers -- kw values not needed */ -//#define BYTES_KW 1 -#define BYTES_VAL 2 -//#define OFFSET_KW 3 -#define OFFSET_VAL 4 -//#define OPER_KW 5 -#define OPER_VAL 6 -//#define RVALUE_KW 7 -#define RVALUE_VAL 8 -//#define RESULT_KW 9 -#define RESULT_VAL 10 - -/* Optional value group numbers */ -#define RELATIVE_KW 11 -#define ENDIAN_KW 12 -#define ENDIAN_VAL 13 -#define STRING_KW 14 -#define STRING_VAL 15 -#define DCE_KW 16 -#define BITMASK_KW 17 -#define BITMASK_VAL 18 - -#define MIN_GROUP 10 -#define MAX_GROUP 19 - -static DetectParseRegex parse_regex; - static int DetectByteMathSetup(DetectEngineCtx *, Signature *, const char *); #ifdef UNITTESTS static void DetectByteMathRegisterTests(void); @@ -131,7 +71,6 @@ void DetectBytemathRegister(void) #ifdef UNITTESTS sigmatch_table[DETECT_BYTEMATH].RegisterTests = DetectByteMathRegisterTests; #endif - DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); } int DetectByteMathDoMatch(DetectEngineThreadCtx *det_ctx, const SigMatchData *smd, @@ -257,277 +196,26 @@ int DetectByteMathDoMatch(DetectEngineThreadCtx *det_ctx, const SigMatchData *sm */ static DetectByteMathData *DetectByteMathParse(DetectEngineCtx *de_ctx, const char *arg, char **rvalue) { - DetectByteMathData *bmd = NULL; - int ret, res; - size_t pcre2len; - char tmp_str[128] = ""; - - ret = DetectParsePcreExec(&parse_regex, arg, 0, 0); - if (ret < MIN_GROUP || ret > MAX_GROUP) { - SCLogError(SC_ERR_PCRE_PARSE, "byte_math parse error; invalid value: ret %" PRId32 - ", string \"%s\"", ret, arg); - goto error; - } - - bmd = SCCalloc(1, sizeof(DetectByteMathData)); - if (unlikely(bmd == NULL)) - goto error; - - /* no of bytes to extract */ - pcre2len = sizeof(tmp_str); - res = pcre2_substring_copy_bynumber( - parse_regex.match, BYTES_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, - "pcre2_substring_copy_bynumber failed " - "for \"nbytes\" value: \"%s\"", - tmp_str); - goto error; - } - - res = ByteExtractStringUint8(&bmd->nbytes, 10, - strlen(tmp_str), - (const char *)tmp_str); - if (res < 1 || bmd->nbytes > 10) { - SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_math invalid bytes " - "value \"%s\" specified.", tmp_str); - goto error; - } - - /* offset */ - pcre2len = sizeof(tmp_str); - res = pcre2_substring_copy_bynumber( - parse_regex.match, OFFSET_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, - "pcre2_substring_copy_bynumber failed " - "for \"offset\" value: \"%s\"", - tmp_str); - goto error; - } - - if (StringParseI32RangeCheck( - &bmd->offset, 10, strlen(tmp_str), (const char *)tmp_str, -65535, 65535) < 0) { - SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_math invalid offset " - "value \"%s\"", tmp_str); - goto error; - } - - /* operator */ - pcre2len = sizeof(tmp_str); - res = pcre2_substring_copy_bynumber( - parse_regex.match, OPER_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, - "pcre2_substring_copy_bynumber failed " - "for \"operator\" value of byte_math: \"%s\"", - tmp_str); - goto error; - } - - if (strcmp(tmp_str, "+") == 0) { - bmd->oper = DETECT_BYTEMATH_OPERATOR_PLUS; - } else if (strcmp(tmp_str, "-") == 0) { - bmd->oper = DETECT_BYTEMATH_OPERATOR_MINUS; - } else if (strcmp(tmp_str, "/") == 0) { - bmd->oper = DETECT_BYTEMATH_OPERATOR_DIVIDE; - } else if (strcmp(tmp_str, "*") == 0) { - bmd->oper = DETECT_BYTEMATH_OPERATOR_MULTIPLY; - } else if (strcmp(tmp_str, "<<") == 0) { - bmd->oper = DETECT_BYTEMATH_OPERATOR_LSHIFT; - } else if (strcmp(tmp_str, ">>") == 0) { - bmd->oper = DETECT_BYTEMATH_OPERATOR_RSHIFT; - } - - /* rvalue */ - pcre2len = sizeof(tmp_str); - res = pcre2_substring_copy_bynumber( - parse_regex.match, RVALUE_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, - "pcre2_substring_copy_bynumber failed " - "for \"rvalue\" to byte_math: \"%s\"", - tmp_str); - goto error; + DetectByteMathData *bmd; + if ((bmd = rs_bytemath_parse(arg)) == NULL) { + SCLogError(SC_ERR_PCRE_PARSE, "invalid bytemath values"); + return NULL; } - if (*tmp_str != '-' && isalpha((unsigned char)*tmp_str)) { + if (bmd->rvalue_str) { if (rvalue == NULL) { SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_math supplied with " "var name for rvalue. \"rvalue\" argument supplied to " "this function must be non-NULL"); goto error; } - *rvalue = SCStrdup(tmp_str); + *rvalue = SCStrdup(bmd->rvalue_str); if (*rvalue == NULL) { goto error; } - } else { - if (ByteExtractStringUint32(&bmd->rvalue, 10, strlen(tmp_str), (const char *)tmp_str) < 0) { - SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_math invalid rvalue " - "value \"%s\"", tmp_str); - goto error; - } } - /* result */ - pcre2len = sizeof(tmp_str); - res = pcre2_substring_copy_bynumber( - parse_regex.match, RESULT_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed " - "for \"result\" to byte_math"); - goto error; - } - if (!isalpha(*tmp_str)) { - SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_math result must be " - "a variable name. Unable to find \"%s\"", tmp_str); - goto error; - } - - bmd->result = SCStrdup(tmp_str); - if (bmd->result == NULL) - goto error; - - /* optional value handling: - * relative - 11 - * endian - 12-13 - * string - 14-15 - * dce - 16 - * bitmask - 17-18 - */ - - if (ret > RELATIVE_KW) { - pcre2len = sizeof(tmp_str); - res = SC_Pcre2SubstringCopy( - parse_regex.match, RELATIVE_KW, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed " - "for byte_math \"relative\" arg"); - goto error; - } - - if (tmp_str[0] != '\0') { - bmd->flags |= DETECT_BYTEMATH_FLAG_RELATIVE; - } - } - - if (ret > ENDIAN_VAL) { - pcre2len = sizeof(tmp_str); - res = SC_Pcre2SubstringCopy( - parse_regex.match, ENDIAN_KW, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed " - "for byte_math \"endian\" arg"); - goto error; - } - - if (tmp_str[0] != '\0') { - bmd->flags |= DETECT_BYTEMATH_FLAG_ENDIAN; - } - - pcre2len = sizeof(tmp_str); - res = SC_Pcre2SubstringCopy( - parse_regex.match, ENDIAN_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed " - "for byte_math \"endian\" value"); - goto error; - } - - /* Since the parse succeeded, there's a value */ - if (strcmp("big", tmp_str) == 0) - bmd->endian = DETECT_BYTEMATH_ENDIAN_BIG; - else if (strcmp("little", tmp_str) == 0) - bmd->endian = DETECT_BYTEMATH_ENDIAN_LITTLE; - } - - if (ret > STRING_VAL) { - pcre2len = sizeof(tmp_str); - res = SC_Pcre2SubstringCopy( - parse_regex.match, STRING_KW, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed " - "for byte_math \"string\" arg"); - goto error; - } - - if (tmp_str[0] != '\0') { - bmd->flags |= DETECT_BYTEMATH_FLAG_STRING; - } - - pcre2len = sizeof(tmp_str); - res = SC_Pcre2SubstringCopy( - parse_regex.match, STRING_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed " - "for byte_math \"string\" value"); - goto error; - } - - /* Since the parse succeeded, there's a value */ - if (strcmp("hex", tmp_str) == 0) { - bmd->base = DETECT_BYTEMATH_BASE_HEX; - } else if (strcmp("oct", tmp_str) == 0) { - bmd->base = DETECT_BYTEMATH_BASE_OCT; - } else if (strcmp("dec", tmp_str) == 0) { - bmd->base = DETECT_BYTEMATH_BASE_DEC; - } - } - - if (ret > DCE_KW) { - pcre2len = sizeof(tmp_str); - res = SC_Pcre2SubstringCopy(parse_regex.match, DCE_KW, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed " - "for byte_math \"dce\" arg"); - goto error; - } - - if (tmp_str[0] != '\0') { - bmd->flags |= DETECT_BYTEMATH_FLAG_ENDIAN; - bmd->endian = DETECT_BYTEMATH_ENDIAN_DCE; - } - } - - if (ret > BITMASK_VAL) { - pcre2len = sizeof(tmp_str); - res = pcre2_substring_copy_bynumber( - parse_regex.match, BITMASK_KW, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed " - "for byte_math \"bitmask\" arg"); - goto error; - } - - if (tmp_str[0] != '\0') { - bmd->flags |= DETECT_BYTEMATH_FLAG_BITMASK; - } - - /* bitmask value*/ - pcre2len = sizeof(tmp_str); - res = pcre2_substring_copy_bynumber( - parse_regex.match, BITMASK_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, - "pcre2_substring_copy_bynumber failed " - "for bitmask value: \"%s\"", - tmp_str); - goto error; - } - - res = ByteExtractStringUint32(&bmd->bitmask_val, 16, strlen(tmp_str), tmp_str); - if (res < 0) { - SCLogError(SC_ERR_INVALID_SIGNATURE, "Unable to extract bitmask " - "value: \"%s\"", tmp_str); - goto error; - } - - /* determine how many trailing 0's are in the bitmask. This will be used - * to rshift the value after applying the bitmask - */ - bmd->bitmask_shift_count = 0; + if (bmd->flags & DETECT_BYTEMATH_FLAG_BITMASK) { if (bmd->bitmask_val) { uint32_t bmask = bmd->bitmask_val; while (!(bmask & 0x1)){ @@ -537,43 +225,6 @@ static DetectByteMathData *DetectByteMathParse(DetectEngineCtx *de_ctx, const ch } } - if (bmd->endian == DETECT_BYTEMATH_ENDIAN_DCE) { - switch (bmd->nbytes) { - case 1: - case 2: - case 4: - break; - default: - SCLogError(SC_ERR_INVALID_SIGNATURE, "nbytes must be 1, 2, or 4 " - "when used with \"dce\"; %d is not valid", bmd->nbytes); - goto error; - break; - } - } - - switch (bmd->oper) { - case DETECT_BYTEMATH_OPERATOR_LSHIFT: - case DETECT_BYTEMATH_OPERATOR_RSHIFT: - /* nbytes has already been validated to be in the range [1, 10] */ - if (bmd->nbytes > 4) { - SCLogError(SC_ERR_INVALID_SIGNATURE, "nbytes must be 1 through 4 (inclusive) " - "when used with \"<<\" or \">>\"; %d is not valid", bmd->nbytes); - goto error; - } - break; - - default: - break; - } - - /* Set defaults for endian and base if needed */ - if (!(bmd->flags & DETECT_BYTEMATH_FLAG_ENDIAN)) { - bmd->endian = DETECT_BYTEMATH_ENDIAN_DEFAULT; - } - if (!(bmd->flags & DETECT_BYTEMATH_FLAG_STRING)) { - bmd->base = DETECT_BYTEMATH_BASE_DEFAULT; - } - return bmd; error: @@ -683,7 +334,7 @@ static int DetectByteMathSetup(DetectEngineCtx *de_ctx, Signature *s, const char goto error; } data->rvalue = index; - data->flags |= DETECT_BYTEMATH_RVALUE_VAR; + data->flags |= DETECT_BYTEMATH_FLAG_RVALUE_VAR; SCFree(rvalue); rvalue = NULL; } @@ -737,14 +388,7 @@ static int DetectByteMathSetup(DetectEngineCtx *de_ctx, Signature *s, const char */ static void DetectByteMathFree(DetectEngineCtx *de_ctx, void *ptr) { - if (ptr != NULL) { - DetectByteMathData *bmd = ptr; - if (bmd->result != NULL) - SCFree((void *)bmd->result); - SCFree(bmd); - } - - return; + rs_bytemath_free(ptr); } /** @@ -1288,11 +932,11 @@ static int DetectByteMathContext01(void) de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any " - "(msg:\"Testing bytemath_body\"; " - "content:\"|00 04 93 F3|\"; " - "content:\"|00 00 00 07|\"; distance:4; within:4;" - "byte_math:bytes 4, offset 0, oper +, rvalue" - "248, result var, relative; sid:1;)"); + "(msg:\"Testing bytemath_body\"; " + "content:\"|00 04 93 F3|\"; " + "content:\"|00 00 00 07|\"; distance:4; within:4;" + "byte_math:bytes 4, offset 0, oper +, rvalue " + "248, result var, relative; sid:1;)"); FAIL_IF(de_ctx->sig_list == NULL); diff --git a/src/detect-bytemath.h b/src/detect-bytemath.h index 6db91c94ab6..099bd81f106 100644 --- a/src/detect-bytemath.h +++ b/src/detect-bytemath.h @@ -24,54 +24,6 @@ #ifndef __DETECT_BYTEMATH_H__ #define __DETECT_BYTEMATH_H__ -/* flags */ -#define DETECT_BYTEMATH_FLAG_RELATIVE 0x01 -#define DETECT_BYTEMATH_FLAG_STRING 0x02 -#define DETECT_BYTEMATH_FLAG_BITMASK 0x04 -#define DETECT_BYTEMATH_FLAG_ENDIAN 0x08 -#define DETECT_BYTEMATH_RVALUE_VAR 0x10 - -/* endian value to be used. Would be stored in DetectByteMathData->endian */ -#define DETECT_BYTEMATH_ENDIAN_NONE 0 -#define DETECT_BYTEMATH_ENDIAN_BIG 1 -#define DETECT_BYTEMATH_ENDIAN_LITTLE 2 -#define DETECT_BYTEMATH_ENDIAN_DCE 3 - -#define DETECT_BYTEMATH_OPERATOR_NONE 1 -#define DETECT_BYTEMATH_OPERATOR_PLUS 2 -#define DETECT_BYTEMATH_OPERATOR_MINUS 3 -#define DETECT_BYTEMATH_OPERATOR_DIVIDE 4 -#define DETECT_BYTEMATH_OPERATOR_MULTIPLY 5 -#define DETECT_BYTEMATH_OPERATOR_LSHIFT 6 -#define DETECT_BYTEMATH_OPERATOR_RSHIFT 7 - -/** - * \brief Holds data related to byte_math keyword. - */ -typedef struct DetectByteMathData_ { - /* local id used by other keywords in the sig to reference this */ - uint8_t local_id; - uint8_t nbytes; - int32_t offset; - - uint32_t rvalue; - - /* "result" variable, if present */ - const char *result; /* consumed */ - - uint8_t flags; - uint8_t endian; - uint8_t base; - uint8_t oper; - - uint32_t bitmask_val; - - uint16_t bitmask_shift_count; - /* unique id used to reference this byte_math keyword */ - uint16_t id; - -} DetectByteMathData; - void DetectBytemathRegister(void); SigMatch *DetectByteMathRetrieveSMVar(const char *, const Signature *); diff --git a/src/detect-engine-content-inspection.c b/src/detect-engine-content-inspection.c index ba2d43f8a31..3687c2f9200 100644 --- a/src/detect-engine-content-inspection.c +++ b/src/detect-engine-content-inspection.c @@ -57,6 +57,8 @@ #include "util-unittest-helper.h" #include "util-profiling.h" +#include "rust-bindings.h" + #ifdef HAVE_LUA #include "util-lua.h" #endif @@ -555,13 +557,12 @@ int DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx DETECT_BYTEMATH_ENDIAN_LITTLE : DETECT_BYTEMATH_ENDIAN_BIG); } uint64_t rvalue; - if (bmd->flags & DETECT_BYTEMATH_RVALUE_VAR) { - rvalue = det_ctx->byte_values[bmd->local_id]; + if (bmd->flags & DETECT_BYTEMATH_FLAG_RVALUE_VAR) { + rvalue = det_ctx->byte_values[bmd->local_id]; } else { - rvalue = bmd->rvalue; + rvalue = bmd->rvalue; } - if (DetectByteMathDoMatch(det_ctx, smd, s, buffer, buffer_len, rvalue,