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-flags.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-flags.h"
31
#include "util-unittest.h"
32
33
#include "rust.h"
34
35
34
#define PARSE_REGEX "(?: *,?!?(?:retain|dup))+"
36
static DetectParseRegex parse_regex;
37
38
static int mqtt_flags_id = 0;
39
40
static int DetectMQTTFlagsMatch(DetectEngineThreadCtx *det_ctx,
41
                               Flow *f, uint8_t flags, void *state,
42
                               void *txv, const Signature *s,
43
                               const SigMatchCtx *ctx);
44
static int DetectMQTTFlagsSetup (DetectEngineCtx *, Signature *, const char *);
45
void MQTTFlagsRegisterTests(void);
46
void DetectMQTTFlagsFree(DetectEngineCtx *de_ctx, void *);
47
48
typedef struct DetectMQTTFlagsData_ {
49
    MQTTFlagState retain, dup;
50
} DetectMQTTFlagsData;
51
52
/**
53
 * \brief Registration function for mqtt.flags: keyword
54
 */
55
void DetectMQTTFlagsRegister (void)
56
34
{
57
34
    sigmatch_table[DETECT_AL_MQTT_FLAGS].name = "mqtt.flags";
58
34
    sigmatch_table[DETECT_AL_MQTT_FLAGS].desc = "match MQTT fixed header flags";
59
34
    sigmatch_table[DETECT_AL_MQTT_FLAGS].url = "/rules/mqtt-keywords.html#mqtt-flags";
60
34
    sigmatch_table[DETECT_AL_MQTT_FLAGS].AppLayerTxMatch = DetectMQTTFlagsMatch;
61
34
    sigmatch_table[DETECT_AL_MQTT_FLAGS].Setup = DetectMQTTFlagsSetup;
62
34
    sigmatch_table[DETECT_AL_MQTT_FLAGS].Free  = DetectMQTTFlagsFree;
63
#ifdef UNITTESTS
64
    sigmatch_table[DETECT_AL_MQTT_FLAGS].RegisterTests = MQTTFlagsRegisterTests;
65
#endif
66
67
34
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
68
69
34
    DetectAppLayerInspectEngineRegister2(
70
34
            "mqtt.flags", ALPROTO_MQTT, SIG_FLAG_TOSERVER, 1, DetectEngineInspectGenericList, NULL);
71
72
34
    mqtt_flags_id = DetectBufferTypeGetByName("mqtt.flags");
73
34
}
74
75
/**
76
 * \internal
77
 * \brief Function to match fixed header flags of an MQTT Tx
78
 *
79
 * \param det_ctx Pointer to the pattern matcher thread.
80
 * \param f       Pointer to the current flow.
81
 * \param flags   Flags.
82
 * \param state   App layer state.
83
 * \param txv     Pointer to the transaction.
84
 * \param s       Pointer to the Signature.
85
 * \param ctx     Pointer to the sigmatch that we will cast into DetectMQTTFlagsData.
86
 *
87
 * \retval 0 no match.
88
 * \retval 1 match.
89
 */
90
static int DetectMQTTFlagsMatch(DetectEngineThreadCtx *det_ctx,
91
                               Flow *f, uint8_t flags, void *state,
92
                               void *txv, const Signature *s,
93
                               const SigMatchCtx *ctx)
94
0
{
95
0
    const DetectMQTTFlagsData *de = (const DetectMQTTFlagsData *)ctx;
96
97
0
    if (!de)
98
0
        return 0;
99
100
0
    return rs_mqtt_tx_has_flags(txv, de->retain, de->dup);
101
0
}
102
103
/**
104
 * \internal
105
 * \brief This function is used to parse options passed via mqtt.flags: keyword
106
 *
107
 * \param rawstr Pointer to the user provided options
108
 *
109
 * \retval de pointer to DetectMQTTFlagsData on success
110
 * \retval NULL on failure
111
 */
112
static DetectMQTTFlagsData *DetectMQTTFlagsParse(const char *rawstr)
113
3.51k
{
114
115
3.51k
    DetectMQTTFlagsData *de = SCCalloc(1, sizeof(DetectMQTTFlagsData));
116
3.51k
    if (unlikely(de == NULL))
117
0
        return NULL;
118
119
3.51k
    pcre2_match_data *match = NULL;
120
3.51k
    int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0);
121
3.51k
    if (ret < 1) {
122
256
        SCLogError("invalid flag definition: %s", rawstr);
123
256
        if (match) {
124
256
            pcre2_match_data_free(match);
125
256
        }
126
256
        SCFree(de);
127
256
        return NULL;
128
256
    }
129
130
3.26k
    de->retain = de->dup = MQTT_DONT_CARE;
131
132
3.26k
    char copy[strlen(rawstr)+1];
133
3.26k
    strlcpy(copy, rawstr, sizeof(copy));
134
3.26k
    char *xsaveptr = NULL;
135
136
    /* Iterate through comma-separated string... */
137
3.26k
    char *flagv = strtok_r(copy, ",", &xsaveptr);
138
9.41k
    while (flagv != NULL) {
139
        /* skip blanks */
140
6.93k
        while (*flagv != '\0' && isblank(*flagv)) {
141
0
            flagv++;
142
0
        }
143
6.93k
        if (strlen(flagv) < 2) {
144
            /* flags have a minimum length */
145
0
            SCLogError("malformed flag value: %s", flagv);
146
0
            goto error;
147
6.93k
        }  else {
148
6.93k
            int offset = 0;
149
6.93k
            MQTTFlagState fs_to_set = MQTT_MUST_BE_SET;
150
6.93k
            if (flagv[0] == '!') {
151
                /* negated flag */
152
6.83k
                offset = 1;  /* skip negation operator during comparison */
153
6.83k
                fs_to_set = MQTT_CANT_BE_SET;
154
6.83k
            }
155
6.93k
            if (strcmp(flagv+offset, "dup") == 0) {
156
3.52k
                if (de->dup != MQTT_DONT_CARE) {
157
527
                    SCLogError("duplicate flag definition: %s", flagv);
158
527
                    goto error;
159
527
                }
160
2.99k
                de->dup = fs_to_set;
161
3.41k
            } else if (strcmp(flagv+offset, "retain") == 0) {
162
3.15k
                if (de->retain != MQTT_DONT_CARE) {
163
0
                    SCLogError("duplicate flag definition: %s", flagv);
164
0
                    goto error;
165
0
                }
166
3.15k
                de->retain = fs_to_set;
167
3.15k
            } else {
168
253
                SCLogError("invalid flag definition: %s", flagv);
169
253
                goto error;
170
253
            }
171
6.93k
        }
172
6.15k
        flagv = strtok_r(NULL, ",", &xsaveptr);
173
6.15k
    }
174
175
3.26k
    pcre2_match_data_free(match);
176
2.48k
    return de;
177
178
780
error:
179
780
    if (match) {
180
780
        pcre2_match_data_free(match);
181
780
    }
182
780
    if (de)
183
780
        SCFree(de);
184
780
    return NULL;
185
3.26k
}
186
187
/**
188
 * \internal
189
 * \brief this function is used to add the parsed type query into the current signature
190
 *
191
 * \param de_ctx pointer to the Detection Engine Context
192
 * \param s pointer to the Current Signature
193
 * \param rawstr pointer to the user provided options
194
 *
195
 * \retval 0 on Success
196
 * \retval -1 on Failure
197
 */
198
static int DetectMQTTFlagsSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
199
3.56k
{
200
3.56k
    DetectMQTTFlagsData *de = NULL;
201
3.56k
    SigMatch *sm = NULL;
202
203
3.56k
    if (DetectSignatureSetAppProto(s, ALPROTO_MQTT) < 0)
204
46
        return -1;
205
206
3.51k
    de = DetectMQTTFlagsParse(rawstr);
207
3.51k
    if (de == NULL)
208
1.03k
        goto error;
209
210
2.48k
    sm = SigMatchAlloc();
211
2.48k
    if (sm == NULL)
212
0
        goto error;
213
214
2.48k
    sm->type = DETECT_AL_MQTT_FLAGS;
215
2.48k
    sm->ctx = (SigMatchCtx *)de;
216
217
2.48k
    SigMatchAppendSMToList(s, sm, mqtt_flags_id);
218
219
2.48k
    return 0;
220
221
1.03k
error:
222
1.03k
    if (de != NULL)
223
0
        SCFree(de);
224
1.03k
    if (sm != NULL)
225
0
        SCFree(sm);
226
1.03k
    return -1;
227
2.48k
}
228
229
/**
230
 * \internal
231
 * \brief this function will free memory associated with DetectMQTTFlagsData
232
 *
233
 * \param de pointer to DetectMQTTFlagsData
234
 */
235
void DetectMQTTFlagsFree(DetectEngineCtx *de_ctx, void *de_ptr)
236
2.48k
{
237
2.48k
    if (de_ptr != NULL)
238
2.48k
        SCFree(de_ptr);
239
2.48k
}
240
241
/*
242
 * ONLY TESTS BELOW THIS COMMENT
243
 */
244
245
#ifdef UNITTESTS
246
/**
247
 * \test MQTTFlagsTestParse01 is a test for a valid value
248
 *
249
 *  \retval 1 on success
250
 *  \retval 0 on failure
251
 */
252
static int MQTTFlagsTestParse01 (void)
253
{
254
    DetectMQTTFlagsData *de = NULL;
255
256
    de = DetectMQTTFlagsParse("retain");
257
    FAIL_IF_NULL(de);
258
    DetectMQTTFlagsFree(NULL, de);
259
260
    de = DetectMQTTFlagsParse("dup");
261
    FAIL_IF_NULL(de);
262
    DetectMQTTFlagsFree(NULL, de);
263
264
    de = DetectMQTTFlagsParse("retain,dup");
265
    FAIL_IF_NULL(de);
266
    DetectMQTTFlagsFree(NULL, de);
267
268
    de = DetectMQTTFlagsParse("dup, retain");
269
    FAIL_IF_NULL(de);
270
    DetectMQTTFlagsFree(NULL, de);
271
272
    PASS;
273
}
274
275
/**
276
 * \test MQTTFlagsTestParse02 is a test for a valid value
277
 *
278
 *  \retval 1 on success
279
 *  \retval 0 on failure
280
 */
281
static int MQTTFlagsTestParse02 (void)
282
{
283
    DetectMQTTFlagsData *de = NULL;
284
    de = DetectMQTTFlagsParse("retain,!dup");
285
    FAIL_IF_NULL(de);
286
    DetectMQTTFlagsFree(NULL, de);
287
288
    PASS;
289
}
290
291
/**
292
 * \test MQTTFlagsTestParse03 is a test for an invalid value
293
 *
294
 *  \retval 1 on success
295
 *  \retval 0 on failure
296
 */
297
static int MQTTFlagsTestParse03 (void)
298
{
299
    DetectMQTTFlagsData *de = NULL;
300
    de = DetectMQTTFlagsParse("ref");
301
    if (de) {
302
        DetectMQTTFlagsFree(NULL, de);
303
        FAIL;
304
    }
305
306
    PASS;
307
}
308
309
/**
310
 * \test MQTTFlagsTestParse04 is a test for an invalid value
311
 *
312
 *  \retval 1 on success
313
 *  \retval 0 on failure
314
 */
315
static int MQTTFlagsTestParse04 (void)
316
{
317
    DetectMQTTFlagsData *de = NULL;
318
    de = DetectMQTTFlagsParse("dup,!");
319
    if (de) {
320
        DetectMQTTFlagsFree(NULL, de);
321
        FAIL;
322
    }
323
324
    PASS;
325
}
326
327
/**
328
 * \test MQTTFlagsTestParse05 is a test for an invalid value
329
 *
330
 *  \retval 1 on success
331
 *  \retval 0 on failure
332
 */
333
static int MQTTFlagsTestParse05 (void)
334
{
335
    DetectMQTTFlagsData *de = NULL;
336
    de = DetectMQTTFlagsParse("dup,!dup");
337
    if (de) {
338
        DetectMQTTFlagsFree(NULL, de);
339
        FAIL;
340
    }
341
342
    de = DetectMQTTFlagsParse("!retain,retain");
343
    if (de) {
344
        DetectMQTTFlagsFree(NULL, de);
345
        FAIL;
346
    }
347
348
    PASS;
349
}
350
351
352
#endif /* UNITTESTS */
353
354
/**
355
 * \brief this function registers unit tests for MQTTFlags
356
 */
357
void MQTTFlagsRegisterTests(void)
358
0
{
359
#ifdef UNITTESTS
360
    UtRegisterTest("MQTTFlagsTestParse01", MQTTFlagsTestParse01);
361
    UtRegisterTest("MQTTFlagsTestParse02", MQTTFlagsTestParse02);
362
    UtRegisterTest("MQTTFlagsTestParse03", MQTTFlagsTestParse03);
363
    UtRegisterTest("MQTTFlagsTestParse04", MQTTFlagsTestParse04);
364
    UtRegisterTest("MQTTFlagsTestParse05", MQTTFlagsTestParse05);
365
#endif /* UNITTESTS */
366
0
}