Coverage Report

Created: 2021-11-03 07:11

/src/suricata/src/detect-datarep.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2018-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
 *  Implements the datarep keyword
24
 */
25
26
#include "suricata-common.h"
27
#include "decode.h"
28
#include "detect.h"
29
#include "threads.h"
30
#include "datasets.h"
31
#include "detect-datarep.h"
32
33
#include "detect-parse.h"
34
#include "detect-engine.h"
35
#include "detect-engine-mpm.h"
36
#include "detect-engine-state.h"
37
38
#include "util-byte.h"
39
#include "util-debug.h"
40
#include "util-print.h"
41
#include "util-misc.h"
42
43
27
#define PARSE_REGEX         "([a-z]+)(?:,\\s*([\\-_A-z0-9\\s\\.]+)){1,4}"
44
static DetectParseRegex parse_regex;
45
46
int DetectDatarepMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *,
47
        const Signature *, const SigMatchCtx *);
48
static int DetectDatarepSetup (DetectEngineCtx *, Signature *, const char *);
49
void DetectDatarepFree (DetectEngineCtx *, void *);
50
51
void DetectDatarepRegister (void)
52
27
{
53
27
    sigmatch_table[DETECT_DATAREP].name = "datarep";
54
27
    sigmatch_table[DETECT_DATAREP].desc = "operate on datasets (experimental)";
55
27
    sigmatch_table[DETECT_DATAREP].url = "/rules/dataset-keywords.html#datarep";
56
27
    sigmatch_table[DETECT_DATAREP].Setup = DetectDatarepSetup;
57
27
    sigmatch_table[DETECT_DATAREP].Free  = DetectDatarepFree;
58
59
27
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
60
27
}
61
62
/*
63
    1 match
64
    0 no match
65
    -1 can't match
66
 */
67
int DetectDatarepBufferMatch(DetectEngineThreadCtx *det_ctx,
68
    const DetectDatarepData *sd,
69
    const uint8_t *data, const uint32_t data_len)
70
0
{
71
0
    if (data == NULL || data_len == 0)
72
0
        return 0;
73
74
0
    DataRepResultType r = DatasetLookupwRep(sd->set, data, data_len, &sd->rep);
75
0
    if (!r.found)
76
0
        return 0;
77
78
0
    switch (sd->op) {
79
0
        case DATAREP_OP_GT:
80
0
            if (r.rep.value > sd->rep.value)
81
0
                return 1;
82
0
            break;
83
0
        case DATAREP_OP_LT:
84
0
            if (r.rep.value < sd->rep.value)
85
0
                return 1;
86
0
            break;
87
0
        case DATAREP_OP_EQ:
88
0
            if (r.rep.value == sd->rep.value)
89
0
                return 1;
90
0
            break;
91
0
    }
92
0
    return 0;
93
0
}
94
95
static int DetectDatarepParse(const char *str, char *cmd, int cmd_len, char *name, int name_len,
96
        enum DatasetTypes *type, char *load, size_t load_size, uint16_t *rep_value,
97
        uint64_t *memcap, uint32_t *hashsize)
98
14.4k
{
99
14.4k
    bool cmd_set = false;
100
14.4k
    bool name_set = false;
101
14.4k
    bool value_set = false;
102
103
14.4k
    char copy[strlen(str)+1];
104
14.4k
    strlcpy(copy, str, sizeof(copy));
105
14.4k
    char *xsaveptr = NULL;
106
14.4k
    char *key = strtok_r(copy, ",", &xsaveptr);
107
89.1k
    while (key != NULL) {
108
138k
        while (*key != '\0' && isblank(*key)) {
109
59.7k
            key++;
110
59.7k
        }
111
79.1k
        char *val = strchr(key, ' ');
112
79.1k
        if (val != NULL) {
113
38.9k
            *val++ = '\0';
114
42.2k
            while (*val != '\0' && isblank(*val)) {
115
3.28k
                val++;
116
3.28k
                SCLogDebug("cmd %s val %s", key, val);
117
3.28k
            }
118
40.1k
        } else {
119
40.1k
            SCLogDebug("cmd %s", key);
120
40.1k
        }
121
122
79.1k
        if (strlen(key) == 0) {
123
275
            goto next;
124
275
        }
125
126
78.8k
        if (!name_set) {
127
14.4k
            if (val) {
128
463
                return -1;
129
463
            }
130
14.0k
            strlcpy(name, key, name_len);
131
14.0k
            name_set = true;
132
64.4k
        } else if (!cmd_set) {
133
13.5k
            if (val) {
134
526
                return -1;
135
526
            }
136
12.9k
            strlcpy(cmd, key, cmd_len);
137
12.9k
            cmd_set = true;
138
50.9k
        } else if (!value_set) {
139
12.7k
            if (val) {
140
743
                return -1;
141
743
            }
142
143
12.0k
            if (StringParseUint16(rep_value, 10, 0, key) < 0)
144
1.22k
                return -1;
145
146
10.8k
            value_set = true;
147
38.1k
        } else {
148
38.1k
            if (val == NULL) {
149
871
                return -1;
150
871
            }
151
152
37.2k
            if (strcmp(key, "type") == 0) {
153
8.50k
                SCLogDebug("type %s", val);
154
155
8.50k
                if (strcmp(val, "md5") == 0) {
156
5.78k
                    *type = DATASET_TYPE_MD5;
157
5.78k
                } else if (strcmp(val, "sha256") == 0) {
158
733
                    *type = DATASET_TYPE_SHA256;
159
1.98k
                } else if (strcmp(val, "string") == 0) {
160
1.32k
                    *type = DATASET_TYPE_STRING;
161
1.32k
                } else {
162
660
                    SCLogDebug("bad type %s", val);
163
660
                    return -1;
164
660
                }
165
166
28.7k
            } else if (strcmp(key, "load") == 0) {
167
15.3k
                SCLogDebug("load %s", val);
168
15.3k
                strlcpy(load, val, load_size);
169
15.3k
            }
170
36.5k
            if (strcmp(key, "memcap") == 0) {
171
675
                if (ParseSizeStringU64(val, memcap) < 0) {
172
434
                    SCLogWarning(SC_ERR_INVALID_VALUE,
173
434
                            "invalid value for memcap: %s,"
174
434
                            " resetting to default",
175
434
                            val);
176
434
                    *memcap = 0;
177
434
                }
178
675
            }
179
36.5k
            if (strcmp(key, "hashsize") == 0) {
180
7.02k
                if (ParseSizeStringU32(val, hashsize) < 0) {
181
878
                    SCLogWarning(SC_ERR_INVALID_VALUE,
182
878
                            "invalid value for hashsize: %s,"
183
878
                            " resetting to default",
184
878
                            val);
185
878
                    *hashsize = 0;
186
878
                }
187
7.02k
            }
188
36.5k
        }
189
190
74.4k
        SCLogDebug("key: %s, value: %s", key, val);
191
192
74.6k
    next:
193
74.6k
        key = strtok_r(NULL, ",", &xsaveptr);
194
74.6k
    }
195
196
9.97k
    if (strlen(load) > 0 && *type == DATASET_TYPE_NOTSET) {
197
1.02k
        SCLogError(SC_ERR_INVALID_SIGNATURE,
198
1.02k
                "if load is used type must be set as well");
199
1.02k
        return 0;
200
1.02k
    }
201
202
8.95k
    if (!name_set || !cmd_set || !value_set) {
203
684
        SCLogError(SC_ERR_INVALID_SIGNATURE,
204
684
                "missing values");
205
684
        return 0;
206
684
    }
207
208
    /* Trim trailing whitespace. */
209
8.77k
    while (strlen(name) > 0 && isblank(name[strlen(name) - 1])) {
210
507
        name[strlen(name) - 1] = '\0';
211
507
    }
212
213
    /* Validate name, spaces are not allowed. */
214
83.7k
    for (size_t i = 0; i < strlen(name); i++) {
215
75.5k
        if (isblank(name[i])) {
216
51
            SCLogError(SC_ERR_INVALID_SIGNATURE,
217
51
                    "spaces not allowed in dataset names");
218
51
            return 0;
219
51
        }
220
75.5k
    }
221
222
8.21k
    return 1;
223
8.27k
}
224
225
/** \brief wrapper around dirname that does leave input untouched */
226
static void GetDirName(const char *in, char *out, size_t outs)
227
7.84k
{
228
7.84k
    if (strlen(in) == 0) {
229
0
        return;
230
0
    }
231
232
7.84k
    size_t size = strlen(in) + 1;
233
7.84k
    char tmp[size];
234
7.84k
    strlcpy(tmp, in, size);
235
236
7.84k
    char *dir = dirname(tmp);
237
7.84k
    BUG_ON(dir == NULL);
238
0
    strlcpy(out, dir, outs);
239
7.84k
    return;
240
7.84k
}
241
242
static int SetupLoadPath(const DetectEngineCtx *de_ctx,
243
        char *load, size_t load_size)
244
8.53k
{
245
8.53k
    SCLogDebug("load %s", load);
246
247
8.53k
    if (PathIsAbsolute(load)) {
248
694
        return 0;
249
694
    }
250
251
7.84k
    bool done = false;
252
7.84k
#ifdef HAVE_LIBGEN_H
253
7.84k
    BUG_ON(de_ctx->rule_file == NULL);
254
255
0
    char dir[PATH_MAX] = "";
256
7.84k
    GetDirName(de_ctx->rule_file, dir, sizeof(dir));
257
258
7.84k
    SCLogDebug("rule_file %s dir %s", de_ctx->rule_file, dir);
259
7.84k
    char path[PATH_MAX];
260
7.84k
    if (snprintf(path, sizeof(path), "%s/%s", dir, load) >= (int)sizeof(path)) // TODO windows path
261
0
        return -1;
262
263
7.84k
    if (SCPathExists(path)) {
264
931
        done = true;
265
931
        strlcpy(load, path, load_size);
266
931
        SCLogDebug("using path '%s' (HAVE_LIBGEN_H)", load);
267
6.91k
    } else {
268
6.91k
        SCLogDebug("path '%s' does not exist (HAVE_LIBGEN_H)", path);
269
6.91k
    }
270
7.84k
#endif
271
7.84k
    if (!done) {
272
6.91k
        char *loadp = DetectLoadCompleteSigPath(de_ctx, load);
273
6.91k
        if (loadp == NULL) {
274
0
            return -1;
275
0
        }
276
6.91k
        SCLogDebug("loadp %s", loadp);
277
278
6.91k
        if (SCPathExists(loadp)) {
279
0
            strlcpy(load, loadp, load_size);
280
0
            SCLogDebug("using path '%s' (non-HAVE_LIBGEN_H)", load);
281
6.91k
        } else {
282
6.91k
            SCLogDebug("path '%s' does not exist (non-HAVE_LIBGEN_H)", loadp);
283
6.91k
        }
284
6.91k
        SCFree(loadp);
285
286
        // TODO try data-dir as well?
287
6.91k
    }
288
7.84k
    return 0;
289
7.84k
}
290
291
static int DetectDatarepSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
292
14.5k
{
293
14.5k
    SigMatch *sm = NULL;
294
14.5k
    char cmd_str[16] = "", name[64] = "";
295
14.5k
    enum DatasetTypes type = DATASET_TYPE_NOTSET;
296
14.5k
    char load[PATH_MAX] = "";
297
14.5k
    uint16_t value = 0;
298
14.5k
    uint64_t memcap = 0;
299
14.5k
    uint32_t hashsize = 0;
300
301
14.5k
    if (DetectBufferGetActiveList(de_ctx, s) == -1) {
302
1
        SCLogError(SC_ERR_INVALID_SIGNATURE,
303
1
                "datarep is only supported for sticky buffers");
304
1
        SCReturnInt(-1);
305
1
    }
306
307
14.5k
    int list = s->init_data->list;
308
14.5k
    if (list == DETECT_SM_LIST_NOTSET) {
309
115
        SCLogError(SC_ERR_INVALID_SIGNATURE,
310
115
                "datarep is only supported for sticky buffers");
311
115
        SCReturnInt(-1);
312
115
    }
313
314
14.4k
    if (!DetectDatarepParse(rawstr, cmd_str, sizeof(cmd_str), name, sizeof(name), &type, load,
315
14.4k
                sizeof(load), &value, &memcap, &hashsize)) {
316
1.75k
        return -1;
317
1.75k
    }
318
319
12.7k
    if (strlen(load) != 0) {
320
8.53k
        if (SetupLoadPath(de_ctx, load, sizeof(load)) != 0)
321
0
            return -1;
322
8.53k
    }
323
324
12.7k
    enum DetectDatarepOp op;
325
12.7k
    if (strcmp(cmd_str,">") == 0) {
326
10.9k
        op = DATAREP_OP_GT;
327
10.9k
    } else if (strcmp(cmd_str,"<") == 0) {
328
292
        op = DATAREP_OP_LT;
329
1.48k
    } else if (strcmp(cmd_str,"==") == 0) {
330
44
        op = DATAREP_OP_EQ;
331
1.44k
    } else {
332
1.44k
        SCLogError(SC_ERR_UNKNOWN_VALUE,
333
1.44k
                "datarep operation \"%s\" is not supported.", cmd_str);
334
1.44k
        return -1;
335
1.44k
    }
336
337
11.2k
    Dataset *set = DatasetGet(name, type, /* no save */ NULL, load, memcap, hashsize);
338
11.2k
    if (set == NULL) {
339
9.07k
        SCLogError(SC_ERR_UNKNOWN_VALUE,
340
9.07k
                "failed to set up datarep set '%s'.", name);
341
9.07k
        return -1;
342
9.07k
    }
343
344
2.18k
    DetectDatarepData *cd = SCCalloc(1, sizeof(DetectDatarepData));
345
2.18k
    if (unlikely(cd == NULL))
346
0
        goto error;
347
348
2.18k
    cd->set = set;
349
2.18k
    cd->op = op;
350
2.18k
    cd->rep.value = value;
351
352
2.18k
    SCLogDebug("cmd %s, name %s",
353
2.18k
        cmd_str, strlen(name) ? name : "(none)");
354
355
    /* Okay so far so good, lets get this into a SigMatch
356
     * and put it in the Signature. */
357
2.18k
    sm = SigMatchAlloc();
358
2.18k
    if (sm == NULL)
359
0
        goto error;
360
361
2.18k
    sm->type = DETECT_DATAREP;
362
2.18k
    sm->ctx = (SigMatchCtx *)cd;
363
2.18k
    SigMatchAppendSMToList(s, sm, list);
364
2.18k
    return 0;
365
366
0
error:
367
0
    if (cd != NULL)
368
0
        SCFree(cd);
369
0
    if (sm != NULL)
370
0
        SCFree(sm);
371
0
    return -1;
372
2.18k
}
373
374
void DetectDatarepFree (DetectEngineCtx *de_ctx, void *ptr)
375
2.18k
{
376
2.18k
    DetectDatarepData *fd = (DetectDatarepData *)ptr;
377
378
2.18k
    if (fd == NULL)
379
0
        return;
380
381
2.18k
    SCFree(fd);
382
2.18k
}