Coverage Report

Created: 2026-05-16 07:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-asn1.c
Line
Count
Source
1
/* Copyright (C) 2020-2022 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 detect-asn1.c
20
 *
21
 * Implements "asn1" keyword
22
 */
23
24
#include "suricata-common.h"
25
#include "decode.h"
26
#include "rust.h"
27
28
#include "detect.h"
29
#include "detect-parse.h"
30
31
#include "flow.h"
32
#include "detect-asn1.h"
33
34
#include "util-unittest.h"
35
#include "util-unittest-helper.h"
36
#include "util-byte.h"
37
#include "util-debug.h"
38
39
static int DetectAsn1Match(DetectEngineThreadCtx *, Packet *,
40
                     const Signature *, const SigMatchCtx *);
41
static int DetectAsn1Setup (DetectEngineCtx *, Signature *, const char *);
42
#ifdef UNITTESTS
43
static void DetectAsn1RegisterTests(void);
44
#endif
45
static void DetectAsn1Free(DetectEngineCtx *, void *);
46
47
/**
48
 * \brief Registration function for asn1
49
 */
50
void DetectAsn1Register(void)
51
73
{
52
73
    sigmatch_table[DETECT_ASN1].name = "asn1";
53
73
    sigmatch_table[DETECT_ASN1].Match = DetectAsn1Match;
54
73
    sigmatch_table[DETECT_ASN1].Setup = DetectAsn1Setup;
55
73
    sigmatch_table[DETECT_ASN1].Free  = DetectAsn1Free;
56
#ifdef UNITTESTS
57
    sigmatch_table[DETECT_ASN1].RegisterTests = DetectAsn1RegisterTests;
58
#endif
59
73
}
60
61
/**
62
 * \brief This function will decode the asn1 data and inspect the resulting
63
 *        nodes to detect if any of the specified checks match this data
64
 *
65
 * \param det_ctx pointer to the detect engine thread context
66
 * \param p pointer to the current packet
67
 * \param s pointer to the signature
68
 * \param ctx pointer to the sigmatch that we will cast into `DetectAsn1Data`
69
 *
70
 * \retval 1 match
71
 * \retval 0 no match
72
 */
73
static int DetectAsn1Match(DetectEngineThreadCtx *det_ctx, Packet *p,
74
                    const Signature *s, const SigMatchCtx *ctx)
75
0
{
76
0
    uint8_t ret = 0;
77
78
0
    if (p->payload_len == 0) {
79
        /* No error, parser done, no data in bounds to decode */
80
0
        return 0;
81
0
    }
82
83
0
    const DetectAsn1Data *ad = (const DetectAsn1Data *)ctx;
84
85
0
    Asn1 *asn1 = rs_asn1_decode(p->payload, p->payload_len, det_ctx->buffer_offset, ad);
86
87
0
    ret = rs_asn1_checks(asn1, ad);
88
89
0
    rs_asn1_free(asn1);
90
91
0
    return ret;
92
0
}
93
94
/**
95
 * \brief This function is used to parse asn1 options passed via asn1: keyword
96
 *
97
 * \param asn1str pointer to the user provided asn1 options
98
 *
99
 * \retval pointer to `DetectAsn1Data` on success
100
 * \retval NULL on failure
101
 */
102
static DetectAsn1Data *DetectAsn1Parse(const char *asn1str)
103
4.01k
{
104
4.01k
    DetectAsn1Data *ad = rs_detect_asn1_parse(asn1str);
105
106
4.01k
    if (ad == NULL) {
107
2.32k
        SCLogError("Malformed asn1 argument: %s", asn1str);
108
2.32k
    }
109
110
4.01k
    return ad;
111
4.01k
}
112
113
/**
114
 * \brief this function is used to add the parsed asn1 data into
115
 *        the current signature
116
 *
117
 * \param de_ctx pointer to the detection engine context
118
 * \param s pointer to the current signature
119
 * \param asn1str pointer to the user provided asn1 options
120
 *
121
 * \retval 0 on success
122
 * \retval -1 on failure
123
 */
124
static int DetectAsn1Setup(DetectEngineCtx *de_ctx, Signature *s, const char *asn1str)
125
4.01k
{
126
4.01k
    DetectAsn1Data *ad = DetectAsn1Parse(asn1str);
127
4.01k
    if (ad == NULL)
128
2.32k
        return -1;
129
130
    /* Okay so far so good, lets get this into a SigMatch
131
     * and put it in the Signature. */
132
1.68k
    SigMatch *sm = SigMatchAlloc();
133
1.68k
    if (sm == NULL) {
134
0
        DetectAsn1Free(de_ctx, ad);
135
0
        return -1;
136
0
    }
137
138
1.68k
    sm->type = DETECT_ASN1;
139
1.68k
    sm->ctx = (SigMatchCtx *)ad;
140
141
1.68k
    SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
142
143
1.68k
    return 0;
144
1.68k
}
145
146
/**
147
 * \brief this function will free memory associated with `DetectAsn1Data`
148
 *
149
 * \param de_ctx pointer to the detection engine context
150
 * \param ptr point to `DetectAsn1Data`
151
 */
152
static void DetectAsn1Free(DetectEngineCtx *de_ctx, void *ptr)
153
1.68k
{
154
1.68k
    DetectAsn1Data *ad = (DetectAsn1Data *)ptr;
155
1.68k
    rs_detect_asn1_free(ad);
156
1.68k
}
157
158
#ifdef UNITTESTS
159
160
/**
161
 * \test DetectAsn1TestReal01 Ensure that all works together
162
 */
163
static int DetectAsn1TestReal01(void)
164
{
165
    uint8_t *buf = (uint8_t *) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
166
                   "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
167
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
168
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
169
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
170
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
171
                   "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
172
                   "Jones""\xA0\x0A\x43\x08""19590717"
173
                   "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
174
                   "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
175
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
176
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
177
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
178
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
179
                   "\x61\x11\x1A\x05""Pablo""\x1A\x01""B""\x1A\x05""Jones"
180
                   "\xA0\x0A\x43\x08""19590717";
181
182
    uint16_t buflen = strlen((char *)buf) - 1;
183
184
    /* Check the start with AA (this is to test the relative_offset keyword) */
185
    uint8_t *buf2 = (uint8_t *) "AA\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
186
                   "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
187
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
188
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
189
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
190
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
191
                   "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
192
                   "Jones""\xA0\x0A\x43\x08""19590717"
193
                   "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
194
                   "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
195
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
196
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
197
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
198
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
199
                   "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones"
200
                   "\xA0\x0A\x43\x08""19590717";
201
202
    uint16_t buflen2 = strlen((char *)buf2) - 1;
203
204
    Packet *p[2];
205
206
    p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
207
    FAIL_IF_NULL(p[0]);
208
    p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP);
209
    FAIL_IF_NULL(p[1]);
210
211
    const char *sigs[3];
212
    sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; "
213
             "content:\"Pablo\"; asn1:absolute_offset 0, "
214
             "oversize_length 130; sid:1;)";
215
    sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; "
216
             "content:\"AA\"; asn1:relative_offset 0, "
217
             "oversize_length 130; sid:2;)";
218
    sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; "
219
             "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)";
220
221
    uint32_t sid[3] = {1, 2, 3};
222
    uint32_t results[2][3] = {
223
                              /* packet 0 match sid 1 */
224
                              {1, 0, 0},
225
                              /* packet 1 match sid 2 */
226
                              {0, 1, 0}};
227
    /* None of the packets should match sid 3 */
228
    FAIL_IF_NOT(UTHGenericTest(p, 2, sigs, sid, (uint32_t *)results, 3) == 1);
229
230
    UTHFreePackets(p, 2);
231
    PASS;
232
}
233
234
/**
235
 * \test DetectAsn1TestReal02 Ensure that all works together
236
 */
237
static int DetectAsn1TestReal02(void)
238
{
239
    int result = 0;
240
    uint8_t *buf = (uint8_t *) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
241
                   "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
242
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
243
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
244
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
245
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
246
                   "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
247
                   "Jones""\xA0\x0A\x43\x08""19590717"
248
                   "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
249
                   "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
250
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
251
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
252
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
253
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
254
                   "\x61\x11\x1A\x05""Pablo""\x1A\x01""B""\x1A\x05""Jones"
255
                   "\xA0\x0A\x43\x08""19590717";
256
257
    uint16_t buflen = strlen((char *)buf) - 1;
258
259
    /* Check the start with AA (this is to test the relative_offset keyword) */
260
    uint8_t *buf2 = (uint8_t *) "AA\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
261
                   "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
262
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
263
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
264
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
265
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
266
                   "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
267
                   "Jones""\xA0\x0A\x43\x08""19590717"
268
                   "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
269
                   "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
270
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
271
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
272
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
273
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
274
                   "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones"
275
                   "\xA0\x0A\x43\x08""19590717";
276
277
    uint16_t buflen2 = strlen((char *)buf2) - 1;
278
279
    Packet *p[2];
280
281
    p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
282
    p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP);
283
284
    if (p[0] == NULL || p[1] == NULL)
285
        goto end;
286
287
    const char *sigs[3];
288
    sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; "
289
             "content:\"Pablo\"; asn1:absolute_offset 0, "
290
             "oversize_length 140; sid:1;)";
291
    sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; "
292
             "content:\"AA\"; asn1:relative_offset 0, "
293
             "oversize_length 140; sid:2;)";
294
    sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; "
295
             "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)";
296
297
    uint32_t sid[3] = {1, 2, 3};
298
299
    uint32_t results[2][3] = {
300
                              {0, 0, 0},
301
                              {0, 0, 0}};
302
    /* None of the packets should match */
303
304
    result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3);
305
306
    UTHFreePackets(p, 2);
307
end:
308
    return result;
309
}
310
311
/**
312
 * \test DetectAsn1TestReal03 Ensure that all works together
313
 */
314
static int DetectAsn1TestReal03(void)
315
{
316
    int result = 0;
317
    uint8_t buf[261] = "";
318
    /* universal class, primitive type, tag_num = 9 (Data type Real) */
319
    buf[0] = '\x09';
320
    /* length, definite form, 2 octets */
321
    buf[1] = '\x82';
322
    /* length is the sum of the following octets (257): */
323
    buf[2] = '\x01';
324
    buf[3] = '\x01';
325
326
    /* Fill the content of the number */
327
    uint16_t i = 4;
328
    for (; i < 257;i++)
329
        buf[i] = '\x05';
330
331
    uint16_t buflen = 261;
332
333
    /* Check the start with AA (this is to test the relative_offset keyword) */
334
    uint8_t *buf2 = (uint8_t *) "AA\x03\x01\xFF";
335
336
    uint16_t buflen2 = 5;
337
338
    Packet *p[2] = { NULL, NULL };
339
340
    p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
341
    p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP);
342
343
    if (p[0] == NULL || p[1] == NULL)
344
        goto end;
345
346
    const char *sigs[3];
347
            /* This should match the first packet */
348
    sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; "
349
             "asn1:absolute_offset 0, double_overflow; sid:1;)";
350
            /* This should match the second packet */
351
    sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; "
352
             "asn1:relative_offset 2, bitstring_overflow,"
353
             "oversize_length 140; sid:2;)";
354
            /* This should match no packet */
355
    sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; "
356
             "asn1: oversize_length 2000; sid:3;)";
357
358
    uint32_t sid[3] = {1, 2, 3};
359
360
    uint32_t results[2][3] = {{1, 0, 0},
361
                              {0, 1, 0}};
362
363
    result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3);
364
365
    UTHFreePackets(p, 2);
366
end:
367
    return result;
368
}
369
370
/**
371
 * \test DetectAsn1TestReal04 like the real test 02, but modified the
372
 *       relative offset to check negative offset values, in this case
373
 *       start decoding from -7 bytes respect the content match "John"
374
 */
375
static int DetectAsn1TestReal04(void)
376
{
377
    int result = 0;
378
    uint8_t *buf = (uint8_t *) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
379
                   "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
380
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
381
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
382
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
383
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
384
                   "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
385
                   "Jones""\xA0\x0A\x43\x08""19590717"
386
                   "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
387
                   "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
388
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
389
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
390
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
391
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
392
                   "\x61\x11\x1A\x05""Pablo""\x1A\x01""B""\x1A\x05""Jones"
393
                   "\xA0\x0A\x43\x08""19590717";
394
395
    uint16_t buflen = strlen((char *)buf) - 1;
396
397
    /* Check the start with AA (this is to test the relative_offset keyword) */
398
    uint8_t *buf2 = (uint8_t *) "AA\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01"
399
                   "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
400
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
401
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
402
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
403
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111"
404
                   "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05"
405
                   "Jones""\xA0\x0A\x43\x08""19590717"
406
                   "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P"
407
                   "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director"
408
                   "\x42\x01\x33\xA1\x0A\x43\x08""19710917"
409
                   "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05"
410
                   "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01"
411
                   "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F"
412
                   "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones"
413
                   "\xA0\x0A\x43\x08""19590717";
414
415
    uint16_t buflen2 = strlen((char *)buf2) - 1;
416
417
    Packet *p[2];
418
419
    p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
420
    p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP);
421
422
    if (p[0] == NULL || p[1] == NULL)
423
        goto end;
424
425
    const char *sigs[3];
426
    sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; "
427
             "content:\"Pablo\"; asn1:absolute_offset 0, "
428
             "oversize_length 140; sid:1;)";
429
    sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; "
430
             "content:\"John\"; asn1:relative_offset -11, "
431
             "oversize_length 140; sid:2;)";
432
    sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; "
433
             "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)";
434
435
    uint32_t sid[3] = {1, 2, 3};
436
437
    uint32_t results[2][3] = {
438
                              {0, 0, 0},
439
                              {0, 0, 0}};
440
    /* None of the packets should match */
441
442
    result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3);
443
444
    UTHFreePackets(p, 2);
445
end:
446
    return result;
447
}
448
449
/**
450
 * \brief this function registers unit tests for DetectAsn1
451
 */
452
static void DetectAsn1RegisterTests(void)
453
{
454
    UtRegisterTest("DetectAsn1TestReal01", DetectAsn1TestReal01);
455
    UtRegisterTest("DetectAsn1TestReal02", DetectAsn1TestReal02);
456
    UtRegisterTest("DetectAsn1TestReal03", DetectAsn1TestReal03);
457
    UtRegisterTest("DetectAsn1TestReal04", DetectAsn1TestReal04);
458
}
459
#endif /* UNITTESTS */