Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-mqtt-qos.c
Line
Count
Source
1
/* Copyright (C) 2020 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Sascha Steinbiss <sascha@steinbiss.name>
22
 */
23
24
#include "suricata-common.h"
25
#include "conf.h"
26
#include "detect.h"
27
#include "detect-parse.h"
28
#include "detect-engine.h"
29
#include "detect-engine-content-inspection.h"
30
#include "detect-mqtt-qos.h"
31
#include "util-byte.h"
32
#include "util-unittest.h"
33
34
#include "rust.h"
35
36
static int mqtt_qos_id = 0;
37
38
static int DetectMQTTQosMatch(DetectEngineThreadCtx *det_ctx,
39
                               Flow *f, uint8_t flags, void *state,
40
                               void *txv, const Signature *s,
41
                               const SigMatchCtx *ctx);
42
static int DetectMQTTQosSetup (DetectEngineCtx *, Signature *, const char *);
43
void MQTTQosRegisterTests(void);
44
void DetectMQTTQosFree(DetectEngineCtx *de_ctx, void *);
45
46
/**
47
 * \brief Registration function for mqtt.qos: keyword
48
 */
49
void DetectMQTTQosRegister (void)
50
34
{
51
34
    sigmatch_table[DETECT_AL_MQTT_QOS].name = "mqtt.qos";
52
34
    sigmatch_table[DETECT_AL_MQTT_QOS].desc = "match MQTT fixed header QOS level";
53
34
    sigmatch_table[DETECT_AL_MQTT_QOS].url = "/rules/mqtt-keywords.html#mqtt-qos";
54
34
    sigmatch_table[DETECT_AL_MQTT_QOS].AppLayerTxMatch = DetectMQTTQosMatch;
55
34
    sigmatch_table[DETECT_AL_MQTT_QOS].Setup = DetectMQTTQosSetup;
56
34
    sigmatch_table[DETECT_AL_MQTT_QOS].Free  = DetectMQTTQosFree;
57
#ifdef UNITTESTS
58
    sigmatch_table[DETECT_AL_MQTT_QOS].RegisterTests = MQTTQosRegisterTests;
59
#endif
60
61
34
    DetectAppLayerInspectEngineRegister2(
62
34
            "mqtt.qos", ALPROTO_MQTT, SIG_FLAG_TOSERVER, 1, DetectEngineInspectGenericList, NULL);
63
64
34
    mqtt_qos_id = DetectBufferTypeGetByName("mqtt.qos");
65
34
}
66
67
/**
68
 * \internal
69
 * \brief Function to match fixed header QOS field of an MQTT Tx
70
 *
71
 * \param det_ctx Pointer to the pattern matcher thread.
72
 * \param f       Pointer to the current flow.
73
 * \param flags   Flags.
74
 * \param state   App layer state.
75
 * \param txv     Pointer to the transaction.
76
 * \param s       Pointer to the Signature.
77
 * \param ctx     Pointer to the sigmatch that we will cast into uint8_t.
78
 *
79
 * \retval 0 no match.
80
 * \retval 1 match.
81
 */
82
static int DetectMQTTQosMatch(DetectEngineThreadCtx *det_ctx,
83
                               Flow *f, uint8_t flags, void *state,
84
                               void *txv, const Signature *s,
85
                               const SigMatchCtx *ctx)
86
6
{
87
6
    const uint8_t *de = (const uint8_t *)ctx;
88
89
6
    if (!de)
90
0
        return 0;
91
92
6
    return rs_mqtt_tx_has_qos(txv, *de);
93
6
}
94
95
/**
96
 * \internal
97
 * \brief This function is used to parse options passed via mqtt.qos: keyword
98
 *
99
 * \param rawstr Pointer to the user provided options
100
 *
101
 * \retval de pointer to DetectMQTTQosData on success
102
 * \retval NULL on failure
103
 */
104
static uint8_t *DetectMQTTQosParse(const char *rawstr)
105
1.85k
{
106
1.85k
    uint8_t *de = NULL;
107
1.85k
    int ret = 0;
108
1.85k
    uint8_t val;
109
110
1.85k
    ret = StringParseU8RangeCheck(&val, 10, 0, rawstr, 0, 2);
111
1.85k
    if (ret <= 0) {
112
173
        SCLogError("invalid MQTT QOS level: %s", rawstr);
113
173
        return NULL;
114
173
    }
115
116
1.68k
    de = SCMalloc(sizeof(uint8_t));
117
1.68k
    if (unlikely(de == NULL))
118
0
        return NULL;
119
1.68k
    *de = val;
120
121
1.68k
    return de;
122
1.68k
}
123
124
/**
125
 * \internal
126
 * \brief this function is used to add the parsed sigmatch  into the current signature
127
 *
128
 * \param de_ctx pointer to the Detection Engine Context
129
 * \param s pointer to the Current Signature
130
 * \param rawstr pointer to the user provided options
131
 *
132
 * \retval 0 on Success
133
 * \retval -1 on Failure
134
 */
135
static int DetectMQTTQosSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
136
1.87k
{
137
1.87k
    uint8_t *de = NULL;
138
1.87k
    SigMatch *sm = NULL;
139
140
1.87k
    if (DetectSignatureSetAppProto(s, ALPROTO_MQTT) < 0)
141
21
        return -1;
142
143
1.85k
    de = DetectMQTTQosParse(rawstr);
144
1.85k
    if (de == NULL)
145
173
        goto error;
146
147
1.68k
    sm = SigMatchAlloc();
148
1.68k
    if (sm == NULL)
149
0
        goto error;
150
151
1.68k
    sm->type = DETECT_AL_MQTT_QOS;
152
1.68k
    sm->ctx = (SigMatchCtx *)de;
153
154
1.68k
    SigMatchAppendSMToList(s, sm, mqtt_qos_id);
155
156
1.68k
    return 0;
157
158
173
error:
159
173
    if (de != NULL)
160
0
        SCFree(de);
161
173
    if (sm != NULL)
162
0
        SCFree(sm);
163
173
    return -1;
164
1.68k
}
165
166
/**
167
 * \internal
168
 * \brief this function will free memory associated with DetectMQTTQosData
169
 *
170
 * \param de pointer to DetectMQTTQosData
171
 */
172
void DetectMQTTQosFree(DetectEngineCtx *de_ctx, void *de_ptr)
173
1.68k
{
174
1.68k
    if (de_ptr != NULL)
175
1.68k
        SCFree(de_ptr);
176
1.68k
}
177
178
/*
179
 * ONLY TESTS BELOW THIS COMMENT
180
 */
181
182
#ifdef UNITTESTS
183
/**
184
 * \test MQTTQosTestParse01 is a test for a valid value
185
 *
186
 *  \retval 1 on success
187
 *  \retval 0 on failure
188
 */
189
static int MQTTQosTestParse01 (void)
190
{
191
    uint8_t *de = NULL;
192
193
    de = DetectMQTTQosParse("0");
194
    FAIL_IF_NULL(de);
195
    FAIL_IF_NOT(*de == 0);
196
    DetectMQTTQosFree(NULL, de);
197
198
    de = DetectMQTTQosParse("   0");
199
    FAIL_IF_NULL(de);
200
    FAIL_IF_NOT(*de == 0);
201
    DetectMQTTQosFree(NULL, de);
202
203
    de = DetectMQTTQosParse("1");
204
    FAIL_IF_NULL(de);
205
    FAIL_IF_NOT(*de == 1);
206
    DetectMQTTQosFree(NULL, de);
207
208
    de = DetectMQTTQosParse("2");
209
    FAIL_IF_NULL(de);
210
    FAIL_IF_NOT(*de == 2);
211
    DetectMQTTQosFree(NULL, de);
212
213
    PASS;
214
}
215
216
/**
217
 * \test MQTTQosTestParse02 is a test for an invalid value
218
 *
219
 *  \retval 1 on success
220
 *  \retval 0 on failure
221
 */
222
static int MQTTQosTestParse02 (void)
223
{
224
    uint8_t *de = NULL;
225
    de = DetectMQTTQosParse("3");
226
    if (de) {
227
        DetectMQTTQosFree(NULL, de);
228
        FAIL;
229
    }
230
231
    PASS;
232
}
233
234
/**
235
 * \test MQTTQosTestParse04 is a test for an invalid value
236
 *
237
 *  \retval 1 on success
238
 *  \retval 0 on failure
239
 */
240
static int MQTTQosTestParse03 (void)
241
{
242
    uint8_t *de = NULL;
243
    de = DetectMQTTQosParse("12");
244
    if (de) {
245
        DetectMQTTQosFree(NULL, de);
246
        FAIL;
247
    }
248
249
    PASS;
250
}
251
252
253
#endif /* UNITTESTS */
254
255
/**
256
 * \brief this function registers unit tests for MQTTQos
257
 */
258
void MQTTQosRegisterTests(void)
259
0
{
260
#ifdef UNITTESTS
261
    UtRegisterTest("MQTTQosTestParse01", MQTTQosTestParse01);
262
    UtRegisterTest("MQTTQosTestParse02", MQTTQosTestParse02);
263
    UtRegisterTest("MQTTQosTestParse03", MQTTQosTestParse03);
264
#endif /* UNITTESTS */
265
0
}