Coverage Report

Created: 2026-09-06 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/detect-modbus.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2014 ANSSI
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 * 3. The name of the author may not be used to endorse or promote products
14
 *    derived from this software without specific prior written permission.
15
 *
16
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
19
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
28
/**
29
 * \file
30
 *
31
 * \author David DIALLO <diallo@et.esiea.fr>
32
 *
33
 * Implements the Modbus function and access keywords
34
 * You can specify a:
35
 * - concrete function like Modbus:
36
 *     function 8, subfunction 4 (diagnostic: Force Listen Only Mode)
37
 * - data (in primary table) register access (r/w) like Modbus:
38
 *     access read coils, address 1000 (.i.e Read coils: at address 1000)
39
 * - write data value at specific address Modbus:
40
 *     access write, address 1500<>2000, value >2000 (Write multiple coils/register:
41
 *     at address between 1500 and 2000 value greater than 2000)
42
 */
43
44
#include "suricata-common.h"
45
46
#include "detect.h"
47
#include "detect-parse.h"
48
#include "detect-engine.h"
49
50
#include "detect-modbus.h"
51
52
#include "util-debug.h"
53
#include "util-byte.h"
54
55
#include "stream-tcp.h"
56
#include "rust.h"
57
58
static int g_modbus_buffer_id = 0;
59
60
/** \internal
61
 *
62
 * \brief this function will free memory associated with DetectModbus
63
 *
64
 * \param ptr pointer to DetectModbus
65
 */
66
2.12k
static void DetectModbusFree(DetectEngineCtx *de_ctx, void *ptr) {
67
2.12k
    SCEnter();
68
2.12k
    if (ptr != NULL) {
69
2.12k
        SCModbusFree(ptr);
70
2.12k
    }
71
2.12k
    SCReturn;
72
2.12k
}
73
74
/** \internal
75
 *
76
 * \brief this function is used to add the parsed "id" option into the current signature
77
 *
78
 * \param de_ctx    Pointer to the Detection Engine Context
79
 * \param s         Pointer to the Current Signature
80
 * \param str       Pointer to the user provided "id" option
81
 *
82
 * \retval 0 on Success or -1 on Failure
83
 */
84
static int DetectModbusSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
85
3.23k
{
86
3.23k
    SCEnter();
87
3.23k
    DetectModbusRust *modbus = NULL;
88
89
3.23k
    if (SCDetectSignatureSetAppProto(s, ALPROTO_MODBUS) != 0)
90
60
        return -1;
91
92
3.17k
    if ((modbus = SCModbusParse(str)) == NULL) {
93
1.04k
        SCLogError("invalid modbus option");
94
1.04k
        goto error;
95
1.04k
    }
96
97
    /* Okay so far so good, lets get this into a SigMatch and put it in the Signature. */
98
2.12k
    if (SCSigMatchAppendSMToList(
99
2.12k
                de_ctx, s, DETECT_MODBUS, (SigMatchCtx *)modbus, g_modbus_buffer_id) == NULL) {
100
0
        goto error;
101
0
    }
102
103
2.12k
    SCReturnInt(0);
104
105
1.04k
error:
106
1.04k
    if (modbus != NULL)
107
0
        DetectModbusFree(de_ctx, modbus);
108
1.04k
    SCReturnInt(-1);
109
2.12k
}
110
111
static int DetectModbusMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
112
        void *txv, const Signature *s, const SigMatchCtx *ctx)
113
0
{
114
0
    return SCModbusInspect(txv, (void *)ctx);
115
0
}
116
117
/**
118
 * \brief Registration function for Modbus keyword
119
 */
120
void DetectModbusRegister(void)
121
39
{
122
39
    sigmatch_table[DETECT_MODBUS].name = "modbus";
123
39
    sigmatch_table[DETECT_MODBUS].desc = "match on various properties of Modbus requests";
124
39
    sigmatch_table[DETECT_MODBUS].url = "/rules/modbus-keyword.html#modbus-keyword";
125
39
    sigmatch_table[DETECT_MODBUS].Match = NULL;
126
39
    sigmatch_table[DETECT_MODBUS].Setup = DetectModbusSetup;
127
39
    sigmatch_table[DETECT_MODBUS].Free = DetectModbusFree;
128
39
    sigmatch_table[DETECT_MODBUS].AppLayerTxMatch = DetectModbusMatch;
129
130
39
    DetectAppLayerInspectEngineRegister(
131
39
            "modbus", ALPROTO_MODBUS, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL);
132
133
39
    g_modbus_buffer_id = DetectBufferTypeGetByName("modbus");
134
39
}