detect: dnp3.func is now a generic integer

Ticket: 7889
pull/14286/head
Philippe Antoine 1 month ago committed by Victor Julien
parent 70a2524d95
commit 85fa894425

@ -12,6 +12,8 @@ This keyword will match on the application function code found in DNP3
request and responses. It can be specified as the integer value or request and responses. It can be specified as the integer value or
the symbolic name of the function code. the symbolic name of the function code.
dnp3_func uses an :ref:`unsigned 8-bits integer <rules-integer-keywords>`.
Syntax Syntax
~~~~~~ ~~~~~~
@ -68,7 +70,7 @@ dnp3_ind
This keyword matches on the DNP3 internal indicator flags in the This keyword matches on the DNP3 internal indicator flags in the
response application header. response application header.
dnp3_ind uses :ref:`unsigned 16-bit integer <rules-integer-keywords>` with bitmasks. dnp3_ind uses an :ref:`unsigned 16-bits integer <rules-integer-keywords>` with bitmasks.
Syntax Syntax
~~~~~~ ~~~~~~

@ -600,7 +600,12 @@
} }
}, },
"function_code": { "function_code": {
"type": "integer" "type": "integer",
"suricata": {
"keywords": [
"dnp3.func"
]
}
}, },
"objects": { "objects": {
"type": "array", "type": "array",
@ -721,7 +726,12 @@
} }
}, },
"function_code": { "function_code": {
"type": "integer" "type": "integer",
"suricata": {
"keywords": [
"dnp3.func"
]
}
}, },
"objects": { "objects": {
"type": "array", "type": "array",
@ -832,7 +842,12 @@
} }
}, },
"function_code": { "function_code": {
"type": "integer" "type": "integer",
"suricata": {
"keywords": [
"dnp3.func"
]
}
}, },
"objects": { "objects": {
"type": "array", "type": "array",

@ -15,7 +15,9 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use crate::detect::uint::{detect_parse_uint_bitflags, DetectBitflagModifier, DetectUintData}; use crate::detect::uint::{
detect_parse_uint_bitflags, detect_parse_uint_enum, DetectBitflagModifier, DetectUintData,
};
use std::ffi::CStr; use std::ffi::CStr;
@ -45,6 +47,48 @@ fn dnp3_detect_ind_parse(s: &str) -> Option<DetectUintData<u16>> {
detect_parse_uint_bitflags::<u16, Dnp3IndFlag>(s, DetectBitflagModifier::Any, false) detect_parse_uint_bitflags::<u16, Dnp3IndFlag>(s, DetectBitflagModifier::Any, false)
} }
#[repr(u8)]
#[derive(EnumStringU8)]
pub enum Dnp3Func {
Confirm = 0,
Read = 1,
Write = 2,
Select = 3,
Operate = 4,
DirectOperate = 5,
DirectOperateNr = 6,
ImmedFreeze = 7,
ImmedFreezeNr = 8,
FreezeClear = 9,
FreezeClearNr = 10,
FreezeAtTime = 11,
FreezeAtTimeNr = 12,
ColdRestart = 13,
WarmRestart = 14,
InitializeData = 15,
InitializeAppl = 16,
StartAppl = 17,
StopAppl = 18,
SaveConfig = 19,
EnableUnsolicited = 20,
DisableUnsolicited = 21,
AssignClass = 22,
DelayMeasure = 23,
RecordCurrentTime = 24,
OpenFile = 25,
CloseFile = 26,
DeleteFile = 27,
GetFileInfo = 28,
AuthenticateFile = 29,
AbortFile = 30,
ActivateConfig = 31,
AutenthicateReq = 32,
AutenthicateErr = 33,
Response = 129,
UnsolicitedResponse = 130,
AuthenticateResp = 131,
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn SCDnp3DetectIndParse( pub unsafe extern "C" fn SCDnp3DetectIndParse(
ustr: *const std::os::raw::c_char, ustr: *const std::os::raw::c_char,
@ -59,6 +103,20 @@ pub unsafe extern "C" fn SCDnp3DetectIndParse(
return std::ptr::null_mut(); return std::ptr::null_mut();
} }
#[no_mangle]
pub unsafe extern "C" fn SCDnp3DetectFuncParse(
ustr: *const std::os::raw::c_char,
) -> *mut DetectUintData<u8> {
let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe
if let Ok(s) = ft_name.to_str() {
if let Some(ctx) = detect_parse_uint_enum::<u8, Dnp3Func>(s) {
let boxed = Box::new(ctx);
return Box::into_raw(boxed) as *mut _;
}
}
return std::ptr::null_mut();
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -84,4 +142,25 @@ mod test {
assert_eq!(ctx.arg1, 0x600); assert_eq!(ctx.arg1, 0x600);
assert!(dnp3_detect_ind_parse("something",).is_none()); assert!(dnp3_detect_ind_parse("something",).is_none());
} }
#[test]
fn dnp3_func_parse() {
let ctx = detect_parse_uint_enum::<u8, Dnp3Func>("0").unwrap();
assert_eq!(ctx.arg1, 0);
let ctx = detect_parse_uint_enum::<u8, Dnp3Func>("1").unwrap();
assert_eq!(ctx.arg1, 1);
let ctx = detect_parse_uint_enum::<u8, Dnp3Func>("254").unwrap();
assert_eq!(ctx.arg1, 254);
let ctx = detect_parse_uint_enum::<u8, Dnp3Func>("255").unwrap();
assert_eq!(ctx.arg1, 255);
let ctx = detect_parse_uint_enum::<u8, Dnp3Func>("confirm").unwrap();
assert_eq!(ctx.arg1, 0);
let ctx = detect_parse_uint_enum::<u8, Dnp3Func>("CONFIRM").unwrap();
assert_eq!(ctx.arg1, 0);
assert!(detect_parse_uint_enum::<u8, Dnp3Func>("").is_none());
assert!(detect_parse_uint_enum::<u8, Dnp3Func>("-1").is_none());
assert!(detect_parse_uint_enum::<u8, Dnp3Func>("-2").is_none());
assert!(detect_parse_uint_enum::<u8, Dnp3Func>("256").is_none());
assert!(detect_parse_uint_enum::<u8, Dnp3Func>("unknown_function_code").is_none());
}
} }

@ -40,62 +40,11 @@ static int g_dnp3_ind_buffer_id = 0;
* The detection struct. * The detection struct.
*/ */
typedef struct DetectDNP3_ { typedef struct DetectDNP3_ {
union { /* Object info for object detection. */
struct { uint8_t obj_group;
/* Function code for function code detection. */ uint8_t obj_variation;
uint8_t function_code;
};
struct {
/* Object info for object detection. */
uint8_t obj_group;
uint8_t obj_variation;
};
};
} DetectDNP3; } DetectDNP3;
/**
* Application function code name to code mappings (Snort compatible).
*/
DNP3Mapping DNP3FunctionNameMap[] = {
{"confirm", 0},
{"read", 1},
{"write", 2},
{"select", 3},
{"operate", 4},
{"direct_operate", 5},
{"direct_operate_nr", 6},
{"immed_freeze", 7},
{"immed_freeze_nr", 8},
{"freeze_clear", 9},
{"freeze_clear_nr", 10},
{"freeze_at_time", 11},
{"freeze_at_time_nr", 12},
{"cold_restart", 13},
{"warm_restart", 14},
{"initialize_data", 15},
{"initialize_appl", 16},
{"start_appl", 17},
{"stop_appl", 18},
{"save_config", 19},
{"enable_unsolicited", 20},
{"disable_unsolicited", 21},
{"assign_class", 22},
{"delay_measure", 23},
{"record_current_time", 24},
{"open_file", 25},
{"close_file", 26},
{"delete_file", 27},
{"get_file_info", 28},
{"authenticate_file", 29},
{"abort_file", 30},
{"activate_config", 31},
{"authenticate_req", 32},
{"authenticate_err", 33},
{"response", 129},
{"unsolicited_response", 130},
{"authenticate_resp", 131}
};
#ifdef UNITTESTS #ifdef UNITTESTS
static void DetectDNP3FuncRegisterTests(void); static void DetectDNP3FuncRegisterTests(void);
static void DetectDNP3ObjRegisterTests(void); static void DetectDNP3ObjRegisterTests(void);
@ -128,64 +77,33 @@ static InspectionBuffer *GetDNP3Data(DetectEngineThreadCtx *det_ctx,
return buffer; return buffer;
} }
/** static void DetectDNP3FuncFree(DetectEngineCtx *de_ctx, void *ptr)
* \brief Parse the provided function name or code to its integer
* value.
*
* If the value passed is a number, it will be checked that it falls
* within the range of valid function codes. If function name is
* passed it will be resolved to its function code.
*
* \retval The function code as an integer if successful, -1 on
* failure.
*/
static int DetectDNP3FuncParseFunctionCode(const char *str, uint8_t *fc)
{ {
if (StringParseUint8(fc, 10, (uint16_t)strlen(str), str) > 0) { SCDetectU8Free(ptr);
return 1;
}
/* Lookup by name. */
for (size_t i = 0;
i < sizeof(DNP3FunctionNameMap) / sizeof(DNP3Mapping); i++) {
if (strcasecmp(str, DNP3FunctionNameMap[i].name) == 0) {
*fc = (uint8_t)(DNP3FunctionNameMap[i].value);
return 1;
}
}
return 0;
} }
static int DetectDNP3FuncSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) static int DetectDNP3FuncSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
{ {
SCEnter(); SCEnter();
DetectDNP3 *dnp3 = NULL;
uint8_t function_code;
if (SCDetectSignatureSetAppProto(s, ALPROTO_DNP3) != 0) if (SCDetectSignatureSetAppProto(s, ALPROTO_DNP3) != 0)
return -1; return -1;
if (!DetectDNP3FuncParseFunctionCode(str, &function_code)) { DetectU8Data *detect = SCDnp3DetectFuncParse(str);
if (detect == NULL) {
SCLogError("Invalid argument \"%s\" supplied to dnp3_func keyword.", str); SCLogError("Invalid argument \"%s\" supplied to dnp3_func keyword.", str);
return -1; return -1;
} }
dnp3 = SCCalloc(1, sizeof(DetectDNP3)); if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_DNP3FUNC, (SigMatchCtx *)detect,
if (unlikely(dnp3 == NULL)) { g_dnp3_match_buffer_id) == NULL) {
goto error;
}
dnp3->function_code = function_code;
if (SCSigMatchAppendSMToList(
de_ctx, s, DETECT_DNP3FUNC, (SigMatchCtx *)dnp3, g_dnp3_match_buffer_id) == NULL) {
goto error; goto error;
} }
SCReturnInt(0); SCReturnInt(0);
error: error:
if (dnp3 != NULL) { if (detect != NULL) {
SCFree(dnp3); DetectDNP3FuncFree(NULL, detect);
} }
SCReturnInt(-1); SCReturnInt(-1);
} }
@ -302,16 +220,15 @@ static int DetectDNP3FuncMatch(DetectEngineThreadCtx *det_ctx,
const SigMatchCtx *ctx) const SigMatchCtx *ctx)
{ {
DNP3Transaction *tx = (DNP3Transaction *)txv; DNP3Transaction *tx = (DNP3Transaction *)txv;
DetectDNP3 *detect = (DetectDNP3 *)ctx; DetectU8Data *detect = (DetectU8Data *)ctx;
int match = 0;
if (flags & STREAM_TOSERVER && tx->is_request) { if (flags & STREAM_TOSERVER && tx->is_request) {
match = detect->function_code == tx->ah.function_code; return DetectU8Match(tx->ah.function_code, detect);
} else if (flags & STREAM_TOCLIENT && !tx->is_request) { } else if (flags & STREAM_TOCLIENT && !tx->is_request) {
match = detect->function_code == tx->ah.function_code; return DetectU8Match(tx->ah.function_code, detect);
} }
return match; return 0;
} }
static int DetectDNP3ObjMatch(DetectEngineThreadCtx *det_ctx, static int DetectDNP3ObjMatch(DetectEngineThreadCtx *det_ctx,
@ -363,7 +280,8 @@ static void DetectDNP3FuncRegister(void)
sigmatch_table[DETECT_DNP3FUNC].Match = NULL; sigmatch_table[DETECT_DNP3FUNC].Match = NULL;
sigmatch_table[DETECT_DNP3FUNC].AppLayerTxMatch = DetectDNP3FuncMatch; sigmatch_table[DETECT_DNP3FUNC].AppLayerTxMatch = DetectDNP3FuncMatch;
sigmatch_table[DETECT_DNP3FUNC].Setup = DetectDNP3FuncSetup; sigmatch_table[DETECT_DNP3FUNC].Setup = DetectDNP3FuncSetup;
sigmatch_table[DETECT_DNP3FUNC].Free = DetectDNP3Free; sigmatch_table[DETECT_DNP3FUNC].Free = DetectDNP3FuncFree;
sigmatch_table[DETECT_DNP3FUNC].flags = SIGMATCH_INFO_UINT8 | SIGMATCH_INFO_ENUM_UINT;
#ifdef UNITTESTS #ifdef UNITTESTS
sigmatch_table[DETECT_DNP3FUNC].RegisterTests = DetectDNP3FuncRegisterTests; sigmatch_table[DETECT_DNP3FUNC].RegisterTests = DetectDNP3FuncRegisterTests;
#endif #endif
@ -472,39 +390,6 @@ void DetectDNP3Register(void)
#include "flow-util.h" #include "flow-util.h"
#include "stream-tcp.h" #include "stream-tcp.h"
static int DetectDNP3FuncParseFunctionCodeTest(void)
{
uint8_t fc;
/* Valid. */
FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("0", &fc));
FAIL_IF(fc != 0);
FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("1", &fc));
FAIL_IF(fc != 1);
FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("254", &fc));
FAIL_IF(fc != 254);
FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("255", &fc));
FAIL_IF(fc != 255);
FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("confirm", &fc));
FAIL_IF(fc != 0);
FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("CONFIRM", &fc));
FAIL_IF(fc != 0);
/* Invalid. */
FAIL_IF(DetectDNP3FuncParseFunctionCode("", &fc));
FAIL_IF(DetectDNP3FuncParseFunctionCode("-1", &fc));
FAIL_IF(DetectDNP3FuncParseFunctionCode("-2", &fc));
FAIL_IF(DetectDNP3FuncParseFunctionCode("256", &fc));
FAIL_IF(DetectDNP3FuncParseFunctionCode("unknown_function_code", &fc));
PASS;
}
static int DetectDNP3FuncTest01(void) static int DetectDNP3FuncTest01(void)
{ {
DetectEngineCtx *de_ctx = DetectEngineCtxInit(); DetectEngineCtx *de_ctx = DetectEngineCtxInit();
@ -519,8 +404,8 @@ static int DetectDNP3FuncTest01(void)
FAIL_IF_NULL(sm); FAIL_IF_NULL(sm);
FAIL_IF_NULL(sm->ctx); FAIL_IF_NULL(sm->ctx);
DetectDNP3 *dnp3func = (DetectDNP3 *)sm->ctx; DetectU8Data *dnp3func = (DetectU8Data *)sm->ctx;
FAIL_IF(dnp3func->function_code != 2); FAIL_IF(dnp3func->arg1 != 2);
DetectEngineCtxFree(de_ctx); DetectEngineCtxFree(de_ctx);
PASS; PASS;
@ -568,8 +453,6 @@ static int DetectDNP3ObjParseTest(void)
static void DetectDNP3FuncRegisterTests(void) static void DetectDNP3FuncRegisterTests(void)
{ {
UtRegisterTest("DetectDNP3FuncParseFunctionCodeTest",
DetectDNP3FuncParseFunctionCodeTest);
UtRegisterTest("DetectDNP3FuncTest01", DetectDNP3FuncTest01); UtRegisterTest("DetectDNP3FuncTest01", DetectDNP3FuncTest01);
} }

Loading…
Cancel
Save