Coverage Report

Created: 2026-08-14 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-flowvar.c
Line
Count
Source
1
/* Copyright (C) 2007-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 Victor Julien <victor@inliniac.net>
22
 *
23
 * Simple flowvar content match part of the detection engine.
24
 */
25
26
#include "suricata-common.h"
27
#include "decode.h"
28
29
#include "detect.h"
30
#include "detect-parse.h"
31
32
#include "detect-content.h"
33
#include "threads.h"
34
#include "flow.h"
35
#include "flow-var.h"
36
#include "pkt-var.h"
37
#include "detect-flowvar.h"
38
39
#include "util-spm.h"
40
#include "util-var-name.h"
41
#include "util-debug.h"
42
#include "util-print.h"
43
44
74
#define PARSE_REGEX         "(.*),(.*)"
45
static DetectParseRegex parse_regex;
46
47
int DetectFlowvarMatch (DetectEngineThreadCtx *, Packet *,
48
        const Signature *, const SigMatchCtx *);
49
static int DetectFlowvarSetup (DetectEngineCtx *, Signature *, const char *);
50
static int DetectFlowvarPostMatch(DetectEngineThreadCtx *det_ctx,
51
        Packet *p, const Signature *s, const SigMatchCtx *ctx);
52
static void DetectFlowvarDataFree(DetectEngineCtx *, void *ptr);
53
54
void DetectFlowvarRegister (void)
55
74
{
56
74
    sigmatch_table[DETECT_FLOWVAR].name = "flowvar";
57
74
    sigmatch_table[DETECT_FLOWVAR].Match = DetectFlowvarMatch;
58
74
    sigmatch_table[DETECT_FLOWVAR].Setup = DetectFlowvarSetup;
59
74
    sigmatch_table[DETECT_FLOWVAR].Free  = DetectFlowvarDataFree;
60
61
    /* post-match for flowvar storage */
62
74
    sigmatch_table[DETECT_FLOWVAR_POSTMATCH].name = "__flowvar__postmatch__";
63
74
    sigmatch_table[DETECT_FLOWVAR_POSTMATCH].Match = DetectFlowvarPostMatch;
64
74
    sigmatch_table[DETECT_FLOWVAR_POSTMATCH].Setup = NULL;
65
74
    sigmatch_table[DETECT_FLOWVAR_POSTMATCH].Free  = DetectFlowvarDataFree;
66
67
74
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
68
74
}
69
70
/**
71
 * \brief this function will SCFree memory associated with DetectFlowvarData
72
 *
73
 * \param cd pointer to DetectContentData
74
 */
75
static void DetectFlowvarDataFree(DetectEngineCtx *de_ctx, void *ptr)
76
26.2k
{
77
26.2k
    if (ptr == NULL)
78
74
        SCReturn;
79
80
26.1k
    DetectFlowvarData *fd = (DetectFlowvarData *)ptr;
81
    /* leave unregistration to pcre keyword */
82
26.1k
    if (!fd->post_match)
83
14.0k
        VarNameStoreUnregister(fd->idx, VAR_TYPE_FLOW_VAR);
84
85
26.1k
    if (fd->name)
86
14.0k
        SCFree(fd->name);
87
26.1k
    if (fd->content)
88
14.0k
        SCFree(fd->content);
89
90
26.1k
    SCFree(fd);
91
26.1k
}
92
93
/*
94
 * returns 0: no match
95
 *         1: match
96
 *        -1: error
97
 */
98
99
int DetectFlowvarMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
100
        const Signature *s, const SigMatchCtx *ctx)
101
2.84k
{
102
2.84k
    int ret = 0;
103
2.84k
    DetectFlowvarData *fd = (DetectFlowvarData *)ctx;
104
105
2.84k
    FlowVar *fv = FlowVarGet(p->flow, fd->idx);
106
2.84k
    if (fv != NULL) {
107
0
        uint8_t *ptr = SpmSearch(fv->data.fv_str.value,
108
0
                                 fv->data.fv_str.value_len,
109
0
                                 fd->content, fd->content_len);
110
0
        if (ptr != NULL)
111
0
            ret = 1;
112
0
    }
113
114
2.84k
    return ret;
115
2.84k
}
116
117
static int DetectFlowvarSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
118
5.71k
{
119
5.71k
    DetectFlowvarData *fd = NULL;
120
5.71k
    SigMatch *sm = NULL;
121
5.71k
    char varname[64], varcontent[64];
122
5.71k
    int res = 0;
123
5.71k
    size_t pcre2len;
124
5.71k
    uint8_t *content = NULL;
125
5.71k
    uint16_t contentlen = 0;
126
5.71k
    uint32_t contentflags = s->init_data->negated ? DETECT_CONTENT_NEGATED : 0;
127
5.71k
    pcre2_match_data *match = NULL;
128
129
5.71k
    int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0);
130
5.71k
    if (ret != 3) {
131
84
        SCLogError("\"%s\" is not a valid setting for flowvar.", rawstr);
132
84
        if (match) {
133
84
            pcre2_match_data_free(match);
134
84
        }
135
84
        return -1;
136
84
    }
137
138
5.62k
    pcre2len = sizeof(varname);
139
5.62k
    res = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)varname, &pcre2len);
140
5.62k
    if (res < 0) {
141
470
        pcre2_match_data_free(match);
142
470
        SCLogError("pcre2_substring_copy_bynumber failed");
143
470
        return -1;
144
470
    }
145
146
5.15k
    pcre2len = sizeof(varcontent);
147
5.15k
    res = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)varcontent, &pcre2len);
148
5.15k
    pcre2_match_data_free(match);
149
5.15k
    if (res < 0) {
150
531
        SCLogError("pcre2_substring_copy_bynumber failed");
151
531
        return -1;
152
531
    }
153
154
4.62k
    int varcontent_index = 0;
155
4.62k
    if (strlen(varcontent) >= 2) {
156
2.81k
        if (varcontent[0] == '"')
157
275
            varcontent_index++;
158
2.81k
        if (varcontent[strlen(varcontent)-1] == '"')
159
500
            varcontent[strlen(varcontent)-1] = '\0';
160
2.81k
    }
161
4.62k
    SCLogDebug("varcontent %s", &varcontent[varcontent_index]);
162
163
4.62k
    res = DetectContentDataParse("flowvar", &varcontent[varcontent_index], &content, &contentlen);
164
4.62k
    if (res == -1)
165
168
        goto error;
166
167
4.45k
    fd = SCMalloc(sizeof(DetectFlowvarData));
168
4.45k
    if (unlikely(fd == NULL))
169
0
        goto error;
170
4.45k
    memset(fd, 0x00, sizeof(*fd));
171
172
4.45k
    fd->content = SCMalloc(contentlen);
173
4.45k
    if (unlikely(fd->content == NULL))
174
0
        goto error;
175
176
4.45k
    memcpy(fd->content, content, contentlen);
177
4.45k
    fd->content_len = contentlen;
178
4.45k
    fd->flags = contentflags;
179
180
4.45k
    fd->name = SCStrdup(varname);
181
4.45k
    if (unlikely(fd->name == NULL))
182
0
        goto error;
183
4.45k
    fd->idx = VarNameStoreRegister(varname, VAR_TYPE_FLOW_VAR);
184
185
    /* Okay so far so good, lets get this into a SigMatch
186
     * and put it in the Signature. */
187
4.45k
    sm = SigMatchAlloc();
188
4.45k
    if (unlikely(sm == NULL))
189
0
        goto error;
190
191
4.45k
    sm->type = DETECT_FLOWVAR;
192
4.45k
    sm->ctx = (SigMatchCtx *)fd;
193
194
4.45k
    SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
195
196
4.45k
    SCFree(content);
197
4.45k
    return 0;
198
199
168
error:
200
168
    if (fd != NULL)
201
0
        DetectFlowvarDataFree(de_ctx, fd);
202
168
    if (sm != NULL)
203
0
        SCFree(sm);
204
168
    if (content != NULL)
205
0
        SCFree(content);
206
168
    return -1;
207
4.45k
}
208
209
/** \brief Store flowvar in det_ctx so we can exec it post-match */
210
int DetectVarStoreMatchKeyValue(DetectEngineThreadCtx *det_ctx,
211
        uint8_t *key, uint16_t key_len,
212
        uint8_t *buffer, uint16_t len, int type)
213
0
{
214
0
    DetectVarList *fs = SCCalloc(1, sizeof(*fs));
215
0
    if (unlikely(fs == NULL))
216
0
        return -1;
217
218
0
    fs->len = len;
219
0
    fs->type = type;
220
0
    fs->buffer = buffer;
221
0
    fs->key = key;
222
0
    fs->key_len = key_len;
223
224
0
    fs->next = det_ctx->varlist;
225
0
    det_ctx->varlist = fs;
226
0
    return 0;
227
0
}
228
229
/** \brief Store flowvar in det_ctx so we can exec it post-match */
230
int DetectVarStoreMatch(DetectEngineThreadCtx *det_ctx,
231
        uint32_t idx,
232
        uint8_t *buffer, uint16_t len, int type)
233
34
{
234
34
    DetectVarList *fs = det_ctx->varlist;
235
236
    /* first check if we have had a previous match for this idx */
237
38
    for ( ; fs != NULL; fs = fs->next) {
238
4
        if (fs->idx == idx) {
239
            /* we're replacing the older store */
240
0
            SCFree(fs->buffer);
241
0
            fs->buffer = NULL;
242
0
            break;
243
0
        }
244
4
    }
245
246
34
    if (fs == NULL) {
247
34
        fs = SCCalloc(1, sizeof(*fs));
248
34
        if (unlikely(fs == NULL))
249
0
            return -1;
250
251
34
        fs->idx = idx;
252
253
34
        fs->next = det_ctx->varlist;
254
34
        det_ctx->varlist = fs;
255
34
    }
256
257
34
    fs->len = len;
258
34
    fs->type = type;
259
34
    fs->buffer = buffer;
260
34
    return 0;
261
34
}
262
263
/** \brief Setup a post-match for flowvar storage
264
 *  We're piggyback riding the DetectFlowvarData struct
265
 */
266
int DetectFlowvarPostMatchSetup(DetectEngineCtx *de_ctx, Signature *s, uint32_t idx)
267
6.05k
{
268
6.05k
    SigMatch *sm = NULL;
269
6.05k
    DetectFlowvarData *fv = NULL;
270
271
6.05k
    fv = SCMalloc(sizeof(DetectFlowvarData));
272
6.05k
    if (unlikely(fv == NULL))
273
0
        goto error;
274
6.05k
    memset(fv, 0x00, sizeof(*fv));
275
276
    /* we only need the idx */
277
6.05k
    fv->idx = idx;
278
6.05k
    fv->post_match = true;
279
280
6.05k
    sm = SigMatchAlloc();
281
6.05k
    if (unlikely(sm == NULL))
282
0
        goto error;
283
284
6.05k
    sm->type = DETECT_FLOWVAR_POSTMATCH;
285
6.05k
    sm->ctx = (SigMatchCtx *)fv;
286
287
6.05k
    SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_POSTMATCH);
288
6.05k
    return 0;
289
0
error:
290
0
    if (fv != NULL)
291
0
        DetectFlowvarDataFree(de_ctx, fv);
292
0
    return -1;
293
6.05k
}
294
295
/** \internal
296
 *  \brief post-match func to store flowvars in the flow
297
 *  \param sm sigmatch containing the idx to store
298
 *  \retval 1 or -1 in case of error
299
 */
300
static int DetectFlowvarPostMatch(
301
        DetectEngineThreadCtx *det_ctx,
302
        Packet *p, const Signature *s, const SigMatchCtx *ctx)
303
7
{
304
7
    DetectVarList *fs, *prev;
305
7
    const DetectFlowvarData *fd;
306
307
7
    if (det_ctx->varlist == NULL)
308
0
        return 1;
309
310
7
    fd = (const DetectFlowvarData *)ctx;
311
312
7
    prev = NULL;
313
7
    fs = det_ctx->varlist;
314
14
    while (fs != NULL) {
315
7
        if (fd->idx == 0 || fd->idx == fs->idx) {
316
7
            SCLogDebug("adding to the flow %u:", fs->idx);
317
            //PrintRawDataFp(stdout, fs->buffer, fs->len);
318
319
7
            if (fs->type == DETECT_VAR_TYPE_FLOW_POSTMATCH && p && p->flow) {
320
7
                FlowVarAddIdValue(p->flow, fs->idx, fs->buffer, fs->len);
321
                /* memory at fs->buffer is now the responsibility of
322
                 * the flowvar code. */
323
7
            } else if (fs->type == DETECT_VAR_TYPE_PKT_POSTMATCH && fs->key && p) {
324
                /* pkt key/value */
325
0
                if (PktVarAddKeyValue(p, (uint8_t *)fs->key, fs->key_len,
326
0
                                         (uint8_t *)fs->buffer, fs->len) == -1)
327
0
                {
328
0
                    SCFree(fs->key);
329
0
                    SCFree(fs->buffer);
330
                    /* the rest of fs is freed below */
331
0
                }
332
0
            } else if (fs->type == DETECT_VAR_TYPE_PKT_POSTMATCH && p) {
333
0
                if (PktVarAdd(p, fs->idx, fs->buffer, fs->len) == -1) {
334
0
                    SCFree(fs->buffer);
335
                    /* the rest of fs is freed below */
336
0
                }
337
0
            }
338
339
7
            if (fs == det_ctx->varlist) {
340
7
                det_ctx->varlist = fs->next;
341
7
                SCFree(fs);
342
7
                fs = det_ctx->varlist;
343
7
            } else {
344
0
                prev->next = fs->next;
345
0
                SCFree(fs);
346
0
                fs = prev->next;
347
0
            }
348
7
        } else {
349
0
            prev = fs;
350
0
            fs = fs->next;
351
0
        }
352
7
    }
353
7
    return 1;
354
7
}
355
356
/** \brief Handle flowvar candidate list in det_ctx: clean up the list
357
 *
358
 *   Only called from DetectVarProcessList() when varlist is not NULL.
359
 */
360
void DetectVarProcessListInternal(DetectVarList *fs, Flow *f, Packet *p)
361
0
{
362
0
    DetectVarList *next;
363
364
0
    do {
365
0
        next = fs->next;
366
367
0
        if (fs->key) {
368
0
            SCFree(fs->key);
369
0
        }
370
0
        SCFree(fs->buffer);
371
0
        SCFree(fs);
372
0
        fs = next;
373
0
    } while (fs != NULL);
374
0
}