From 55c50812407556c1276c3e6b6f7e98e869427214 Mon Sep 17 00:00:00 2001 From: DIALLO David Date: Tue, 22 Jul 2014 09:49:58 +0200 Subject: [PATCH] Detect-engine: Add Modbus detection engine Management of Modbus Tx Based on DNS source code. Signed-off-by: David DIALLO --- src/Makefile.am | 1 + src/detect-engine-modbus.c | 1345 ++++++++++++++++++++++++++++++++++++ src/detect-engine-modbus.h | 41 ++ src/detect-engine-state.h | 1 + src/detect-engine.c | 19 +- src/runmode-unittests.c | 2 + 6 files changed, 1408 insertions(+), 1 deletion(-) create mode 100644 src/detect-engine-modbus.c create mode 100644 src/detect-engine-modbus.h diff --git a/src/Makefile.am b/src/Makefile.am index 407018119a..d788afdc57 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -92,6 +92,7 @@ detect-engine.c detect-engine.h \ detect-engine-content-inspection.c detect-engine-content-inspection.h \ detect-engine-dcepayload.c detect-engine-dcepayload.h \ detect-engine-dns.c detect-engine-dns.h \ +detect-engine-modbus.c detect-engine-modbus.h \ detect-engine-event.c detect-engine-event.h \ detect-engine-file.c detect-engine-file.h \ detect-engine-hcbd.c detect-engine-hcbd.h \ diff --git a/src/detect-engine-modbus.c b/src/detect-engine-modbus.c new file mode 100644 index 0000000000..cced496634 --- /dev/null +++ b/src/detect-engine-modbus.c @@ -0,0 +1,1345 @@ +/* + * 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 "app-layer-modbus.h" + +#include "detect.h" +#include "detect-modbus.h" + +#include "detect-engine-modbus.h" + +#include "flow.h" + +#include "util-debug.h" + +/** \internal + * + * \brief Value match detection code + * + * \param value Modbus value context (min, max and mode) + * \param min Minimum value to compare + * \param inter Interval or maximum (min + inter) value to compare + * + * \retval 1 match or 0 no match + */ +static int DetectEngineInspectModbusValueMatch(DetectModbusValue *value, + uint16_t min, + uint16_t inter) +{ + SCEnter(); + uint16_t max = min + inter; + + int ret = 0; + + switch (value->mode) { + case DETECT_MODBUS_EQ: + if ((value->min >= min) && (value->min <= max)) + ret = 1; + break; + + case DETECT_MODBUS_LT: + if (value->min > min) + ret = 1; + break; + + case DETECT_MODBUS_GT: + if (value->min < max) + ret = 1; + break; + + case DETECT_MODBUS_RA: + if ((value->max > min) && (value->min < max)) + ret = 1; + break; + } + + SCReturnInt(ret); +} + +/** \internal + * + * \brief Do data (and address) inspection & validation for a signature + * + * \param tx Pointer to Modbus Transaction + * \param address Address inspection + * \param data Pointer to data signature structure to match + * + * \retval 0 no match or 1 match + */ +static int DetectEngineInspectModbusData(ModbusTransaction *tx, + uint16_t address, + DetectModbusValue *data) +{ + SCEnter(); + uint16_t offset, value = 0, type = tx->type; + + if (type & MODBUS_TYP_SINGLE) { + /* Output/Register(s) Value */ + if (type & MODBUS_TYP_COILS) + value = (tx->data[0])? 1 : 0; + else + value = tx->data[0]; + } else if (type & MODBUS_TYP_MULTIPLE) { + int i, size = (int) sizeof(tx->data); + + offset = address - (tx->write.address + 1); + + /* In case of Coils, offset is in bit (convert in byte) */ + if (type & MODBUS_TYP_COILS) + offset >>= 3; + + for (i=0; i< size; i++) { + /* Select the correct register/coils amongst the output value */ + if (!(offset--)) { + value = tx->data[i]; + break; + } + } + + /* In case of Coils, offset is now in the bit is the rest of previous convert */ + if (type & MODBUS_TYP_COILS) { + offset = (address - (tx->write.address + 1)) & 0x7; + value = (value >> offset) & 0x1; + } + } else { + /* It is not possible to define the value that is writing for Mask */ + /* Write Register function because the current content is not available.*/ + SCReturnInt(0); + } + + SCReturnInt(DetectEngineInspectModbusValueMatch(data, value, 0)); +} + +/** \internal + * + * \brief Do address inspection & validation for a signature + * + * \param tx Pointer to Modbus Transaction + * \param address Pointer to address signature structure to match + * \param access Access mode (READ or WRITE) + * + * \retval 0 no match or 1 match + */ +static int DetectEngineInspectModbusAddress(ModbusTransaction *tx, + DetectModbusValue *address, + uint8_t access) +{ + SCEnter(); + int ret = 0; + + /* Check if read/write address of request is at/in the address range of signature */ + if (access == MODBUS_TYP_READ) { + /* In the PDU Coils are addresses starting at zero */ + /* therefore Coils numbered 1-16 are addressed as 0-15 */ + ret = DetectEngineInspectModbusValueMatch(address, + tx->read.address + 1, + tx->read.quantity - 1); + } else { + /* In the PDU Registers are addresses starting at zero */ + /* therefore Registers numbered 1-16 are addressed as 0-15 */ + if (tx->type & MODBUS_TYP_SINGLE) + ret = DetectEngineInspectModbusValueMatch(address, + tx->write.address + 1, + 0); + else + ret = DetectEngineInspectModbusValueMatch(address, + tx->write.address + 1, + tx->write.quantity - 1); + } + + SCReturnInt(ret); +} + +/** \brief Do the content inspection & validation for a signature + * + * \param de_ctx Detection engine context + * \param det_ctx Detection engine thread context + * \param s Signature to inspect ( and sm: SigMatch to inspect) + * \param f Flow + * \param flags App layer flags + * \param alstate App layer state + * \param txv Pointer to Modbus Transaction structure + * + * \retval 0 no match or 1 match + */ +int DetectEngineInspectModbus(ThreadVars *tv, + DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, + Signature *s, + Flow *f, + uint8_t flags, + void *alstate, + void *txv, + uint64_t tx_id) +{ + SCEnter(); + ModbusTransaction *tx = (ModbusTransaction *)txv; + SigMatch *sm = s->sm_lists[DETECT_SM_LIST_MODBUS_MATCH]; + DetectModbus *modbus = (DetectModbus *) sm->ctx; + + int ret = 0; + + if (modbus == NULL) { + SCLogDebug("no modbus state, no match"); + SCReturnInt(0); + } + + if (modbus->type == MODBUS_TYP_NONE) { + if (modbus->category == MODBUS_CAT_NONE) { + if (modbus->function == tx->function) { + if (modbus->subfunction != NULL) { + SCLogDebug("looking for Modbus server function %d and subfunction %d", + modbus->function, *(modbus->subfunction)); + ret = (*(modbus->subfunction) == (tx->subFunction))? 1 : 0; + } else { + SCLogDebug("looking for Modbus server function %d", modbus->function); + ret = 1; + } + } + } else { + SCLogDebug("looking for Modbus category function %d", modbus->category); + ret = (tx->category & modbus->category)? 1 : 0; + } + } else { + uint8_t access = modbus->type & MODBUS_TYP_ACCESS_MASK; + uint8_t function = modbus->type & MODBUS_TYP_ACCESS_FUNCTION_MASK; + + if ((access & tx->type) && ((function == MODBUS_TYP_NONE) || (function & tx->type))) { + if (modbus->address != NULL) { + ret = DetectEngineInspectModbusAddress(tx, modbus->address, access); + + if (ret && (modbus->data != NULL)) { + ret = DetectEngineInspectModbusData(tx, modbus->address->min, modbus->data); + } + } else { + SCLogDebug("looking for Modbus access type %d and function type %d", access, function); + ret = 1; + } + } + } + + SCReturnInt(ret); +} + +#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 */ 0x00, + /* 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, 0x05, + /* 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 */ 0x00, + /* 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 */ 0x12}; + +/** \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; + + int result = 0; + + 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(); + if (de_ctx == NULL) + goto end; + + 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;)"); + + if (s == NULL) + goto end; + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, + readWriteMultipleRegistersReq, sizeof(readWriteMultipleRegistersReq)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ModbusState *modbus_state = f.alstate; + if (modbus_state == NULL) { + printf("no modbus state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (!(PacketAlertCheck(p, 1))) { + printf("sid 1 didn't match but should have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \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; + + int result = 0; + + 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(); + if (de_ctx == NULL) + goto end; + + 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;)"); + + if (s == NULL) + goto end; + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, forceListenOnlyMode, sizeof(forceListenOnlyMode)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ModbusState *modbus_state = f.alstate; + if (modbus_state == NULL) { + printf("no modbus state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (!(PacketAlertCheck(p, 1))) { + printf("sid 1 didn't match but should have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \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; + + int result = 0; + + 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(); + if (de_ctx == NULL) + goto end; + + 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;)"); + + if (s == NULL) + goto end; + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, + encapsulatedInterfaceTransport, sizeof(encapsulatedInterfaceTransport)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ModbusState *modbus_state = f.alstate; + if (modbus_state == NULL) { + printf("no modbus state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (!(PacketAlertCheck(p, 1))) { + printf("sid 1 didn't match but should have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \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; + + int result = 0; + + 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(); + if (de_ctx == NULL) + goto end; + + 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;)"); + + if (s == NULL) + goto end; + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, unassigned, sizeof(unassigned)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ModbusState *modbus_state = f.alstate; + if (modbus_state == NULL) { + printf("no modbus state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (!(PacketAlertCheck(p, 1))) { + printf("sid 1 didn't match but should have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \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; + + int result = 0; + + 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(); + if (de_ctx == NULL) + goto end; + + 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;)"); + + if (s == NULL) + goto end; + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, + readCoilsReq, sizeof(readCoilsReq)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ModbusState *modbus_state = f.alstate; + if (modbus_state == NULL) { + printf("no modbus state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (!(PacketAlertCheck(p, 1))) { + printf("sid 1 didn't match but should have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \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; + + int result = 0; + + 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(); + if (de_ctx == NULL) + goto end; + + 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;)"); + + if (s == NULL) + goto end; + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, + readInputsRegistersReq, sizeof(readInputsRegistersReq)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ModbusState *modbus_state = f.alstate; + if (modbus_state == NULL) { + printf("no modbus state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (!(PacketAlertCheck(p, 1))) { + printf("sid 1 didn't match but should have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \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; + + int result = 0; + + 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(); + if (de_ctx == NULL) + goto end; + + 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;)"); + + if (s == NULL) + goto end; + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, readCoilsReq, sizeof(readCoilsReq)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ModbusState *modbus_state = f.alstate; + if (modbus_state == NULL) { + printf("no modbus state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (!(PacketAlertCheck(p, 1))) { + printf("sid 1 didn't match but should have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \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; + + int result = 0; + + 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(); + if (de_ctx == NULL) + goto end; + + 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;)"); + + if (s == NULL) + goto end; + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, + readInputsRegistersReq, sizeof(readInputsRegistersReq)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ModbusState *modbus_state = f.alstate; + if (modbus_state == NULL) { + printf("no modbus state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (PacketAlertCheck(p, 1)) { + printf("sid 1 did match but should not have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 2))) { + printf("sid 2 didn't match but should have: "); + goto end; + } + + if (PacketAlertCheck(p, 3)) { + printf("sid 3 did match but should not have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 4))) { + printf("sid 4 didn't match but should have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 5))) { + printf("sid 5 didn't match but should have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 6))) { + printf("sid 6 didn't match but should have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 7))) { + printf("sid 7 didn't match but should have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 8))) { + printf("sid 8 didn't match but should have: "); + goto end; + } + + if (PacketAlertCheck(p, 9)) { + printf("sid 9 did match but should not have: "); + goto end; + } + + if (PacketAlertCheck(p, 10)) { + printf("sid 10 did match but should not have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \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; + + int result = 0; + + 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(); + if (de_ctx == NULL) + goto end; + + 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;)"); + + if (s == NULL) + goto end; + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, + readWriteMultipleRegistersReq, sizeof(readWriteMultipleRegistersReq)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ModbusState *modbus_state = f.alstate; + if (modbus_state == NULL) { + printf("no modbus state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (PacketAlertCheck(p, 1)) { + printf("sid 1 did match but should not have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 2))) { + printf("sid 2 didn't match but should have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 3))) { + printf("sid 3 didn't match but should have: "); + goto end; + } + + if (PacketAlertCheck(p, 4)) { + printf("sid 4 did match but should not have: "); + goto end; + } + + if (PacketAlertCheck(p, 5)) { + printf("sid 5 did match but should not have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 6))) { + printf("sid 6 didn't match but should have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 7))) { + printf("sid 7 didn't match but should have: "); + goto end; + } + + if (PacketAlertCheck(p, 8)) { + printf("sid 8 did match but should not have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 9))) { + printf("sid 9 didn't match but should have: "); + goto end; + } + + if (PacketAlertCheck(p, 10)) { + printf("sid 10 did match but should not have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} +#endif /* UNITTESTS */ + +void DetectEngineInspectModbusRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("DetectEngineInspectModbusTest01 - Code function", DetectEngineInspectModbusTest01, 1); + UtRegisterTest("DetectEngineInspectModbusTest02 - code function and code subfunction", DetectEngineInspectModbusTest02, 1); + UtRegisterTest("DetectEngineInspectModbusTest03 - Function category", DetectEngineInspectModbusTest03, 1); + UtRegisterTest("DetectEngineInspectModbusTest04 - Negative function category", DetectEngineInspectModbusTest04, 1); + UtRegisterTest("DetectEngineInspectModbusTest05 - Access type", DetectEngineInspectModbusTest05, 1); + UtRegisterTest("DetectEngineInspectModbusTest06 - Access function", DetectEngineInspectModbusTest06, 1); + UtRegisterTest("DetectEngineInspectModbusTest07 - Read access at an address", DetectEngineInspectModbusTest07, 1); + UtRegisterTest("DetectEngineInspectModbusTest08 - Read access at a range of address", DetectEngineInspectModbusTest08, 1); + UtRegisterTest("DetectEngineInspectModbusTest09 - Write access at an address a range of value", DetectEngineInspectModbusTest09, 1); +#endif /* UNITTESTS */ + return; +} diff --git a/src/detect-engine-modbus.h b/src/detect-engine-modbus.h new file mode 100644 index 0000000000..e140f975a5 --- /dev/null +++ b/src/detect-engine-modbus.h @@ -0,0 +1,41 @@ +/* + * 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(ThreadVars *, DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *, Signature *, + Flow *, uint8_t, void *, void *, uint64_t); + +void DetectEngineInspectModbusRegisterTests(void); +#endif /* __DETECT_ENGINE_MODBUS_H__ */ diff --git a/src/detect-engine-state.h b/src/detect-engine-state.h index cd3e63ea4c..9f49755118 100644 --- a/src/detect-engine-state.h +++ b/src/detect-engine-state.h @@ -76,6 +76,7 @@ #define DE_STATE_FLAG_SIG_CANT_MATCH (1 << 16) #define DE_STATE_FLAG_DNSQUERY_INSPECT (1 << 17) #define DE_STATE_FLAG_APP_EVENT_INSPECT (1 << 18) +#define DE_STATE_FLAG_MODBUS_INSPECT (1 << 19) /* state flags */ #define DETECT_ENGINE_STATE_FLAG_FILE_STORE_DISABLED 0x0001 diff --git a/src/detect-engine.c b/src/detect-engine.c index d9655762aa..11f117adda 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -59,6 +59,7 @@ #include "detect-engine-hrhhd.h" #include "detect-engine-file.h" #include "detect-engine-dns.h" +#include "detect-engine-modbus.h" #include "detect-engine.h" #include "detect-engine-state.h" @@ -254,6 +255,14 @@ void DetectEngineRegisterAppInspectionEngines(void) DE_STATE_FLAG_FILE_TS_INSPECT, 0, DetectFileInspectSmtp }, + /* Modbus */ + { IPPROTO_TCP, + ALPROTO_MODBUS, + DETECT_SM_LIST_MODBUS_MATCH, + DE_STATE_FLAG_MODBUS_INSPECT, + DE_STATE_FLAG_MODBUS_INSPECT, + 0, + DetectEngineInspectModbus }, }; struct tmp_t data_toclient[] = { @@ -305,7 +314,15 @@ void DetectEngineRegisterAppInspectionEngines(void) DE_STATE_FLAG_HSCD_INSPECT, DE_STATE_FLAG_HSCD_INSPECT, 1, - DetectEngineInspectHttpStatCode } + DetectEngineInspectHttpStatCode }, + /* Modbus */ + { IPPROTO_TCP, + ALPROTO_MODBUS, + DETECT_SM_LIST_MODBUS_MATCH, + DE_STATE_FLAG_MODBUS_INSPECT, + DE_STATE_FLAG_MODBUS_INSPECT, + 0, + DetectEngineInspectModbus } }; size_t i; diff --git a/src/runmode-unittests.c b/src/runmode-unittests.c index 479e1323d4..d77f78b689 100644 --- a/src/runmode-unittests.c +++ b/src/runmode-unittests.c @@ -50,6 +50,7 @@ #include "detect-engine-hrhhd.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" @@ -249,6 +250,7 @@ void RunUnittests(int list_unittests, char *regex_arg) DetectEngineHttpUARegisterTests(); DetectEngineHttpHHRegisterTests(); DetectEngineHttpHRHRegisterTests(); + DetectEngineInspectModbusRegisterTests(); DetectEngineRegisterTests(); SCLogRegisterTests(); MagicRegisterTests();