Coverage Report

Created: 2025-07-23 07:29

/src/suricata7/src/detect-dnp3.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2015-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
#include "suricata-common.h"
19
20
#include "stream.h"
21
22
#include "detect.h"
23
#include "detect-parse.h"
24
#include "detect-dnp3.h"
25
#include "detect-engine.h"
26
#include "detect-engine-mpm.h"
27
#include "detect-engine-prefilter.h"
28
#include "detect-engine-content-inspection.h"
29
30
#include "app-layer-dnp3.h"
31
#include "util-byte.h"
32
33
static int g_dnp3_match_buffer_id = 0;
34
static int g_dnp3_data_buffer_id = 0;
35
36
/**
37
 * The detection struct.
38
 */
39
typedef struct DetectDNP3_ {
40
    union {
41
        struct {
42
            /* Function code for function code detection. */
43
            uint8_t  function_code;
44
        };
45
        struct {
46
            /* Internal indicator flags for IIN detection. */
47
            uint16_t ind_flags;
48
        };
49
        struct {
50
            /* Object info for object detection. */
51
            uint8_t  obj_group;
52
            uint8_t  obj_variation;
53
        };
54
    };
55
} DetectDNP3;
56
57
/**
58
 * Indicator names to value mappings (Snort compatible).
59
 */
60
DNP3Mapping DNP3IndicatorsMap[] = {
61
    {"device_restart",        0x8000},
62
    {"device_trouble",        0x4000},
63
    {"local_control",         0x2000},
64
    {"need_time",             0x1000},
65
    {"class_3_events",        0x0800},
66
    {"class_2_events",        0x0400},
67
    {"class_1_events",        0x0200},
68
    {"all_stations",          0x0100},
69
70
    {"reserved_1",            0x0080},
71
    {"reserved_2",            0x0040},
72
    {"config_corrupt",        0x0020},
73
    {"already_executing",     0x0010},
74
    {"event_buffer_overflow", 0x0008},
75
    {"parameter_error",       0x0004},
76
    {"object_unknown",        0x0002},
77
    {"no_func_code_support",  0x0001},
78
79
    {NULL, 0},
80
};
81
82
/**
83
 * Application function code name to code mappings (Snort compatible).
84
 */
85
DNP3Mapping DNP3FunctionNameMap[] = {
86
    {"confirm",              0},
87
    {"read",                 1},
88
    {"write",                2},
89
    {"select",               3},
90
    {"operate",              4},
91
    {"direct_operate",       5},
92
    {"direct_operate_nr",    6},
93
    {"immed_freeze",         7},
94
    {"immed_freeze_nr",      8},
95
    {"freeze_clear",         9},
96
    {"freeze_clear_nr",      10},
97
    {"freeze_at_time",       11},
98
    {"freeze_at_time_nr",    12},
99
    {"cold_restart",         13},
100
    {"warm_restart",         14},
101
    {"initialize_data",      15},
102
    {"initialize_appl",      16},
103
    {"start_appl",           17},
104
    {"stop_appl",            18},
105
    {"save_config",          19},
106
    {"enable_unsolicited",   20},
107
    {"disable_unsolicited",  21},
108
    {"assign_class",         22},
109
    {"delay_measure",        23},
110
    {"record_current_time",  24},
111
    {"open_file",            25},
112
    {"close_file",           26},
113
    {"delete_file",          27},
114
    {"get_file_info",        28},
115
    {"authenticate_file",    29},
116
    {"abort_file",           30},
117
    {"activate_config",      31},
118
    {"authenticate_req",     32},
119
    {"authenticate_err",     33},
120
    {"response",             129},
121
    {"unsolicited_response", 130},
122
    {"authenticate_resp",    131}
123
};
124
125
#ifdef UNITTESTS
126
static void DetectDNP3FuncRegisterTests(void);
127
static void DetectDNP3IndRegisterTests(void);
128
static void DetectDNP3ObjRegisterTests(void);
129
#endif
130
131
/**
132
 * \brief Utility function to trim leading and trailing whitespace
133
 *     from a string.
134
 */
135
static char *TrimString(char *str)
136
96
{
137
96
    char *end = str + strlen(str) - 1;
138
230
    while (isspace(*str)) {
139
230
        str++;
140
230
    }
141
313
    while (end > str && isspace(*end)) {
142
217
        end--;
143
217
    }
144
96
    *(end + 1) = '\0';
145
96
    return str;
146
96
}
147
148
static InspectionBuffer *GetDNP3Data(DetectEngineThreadCtx *det_ctx,
149
        const DetectEngineTransforms *transforms,
150
        Flow *_f, const uint8_t flow_flags,
151
        void *txv, const int list_id)
152
296
{
153
296
    SCLogDebug("list_id %d", list_id);
154
296
    InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
155
296
    if (buffer->inspect == NULL) {
156
281
        DNP3Transaction *tx = (DNP3Transaction *)txv;
157
281
        SCLogDebug("tx %p", tx);
158
159
281
        if ((flow_flags & STREAM_TOSERVER && !tx->is_request) ||
160
281
                (flow_flags & STREAM_TOCLIENT && tx->is_request)) {
161
0
            return NULL;
162
0
        }
163
164
281
        if (tx->buffer == NULL || tx->buffer_len == 0) {
165
0
            return NULL;
166
0
        }
167
168
281
        SCLogDebug("tx %p data %p data_len %u", tx, tx->buffer, tx->buffer_len);
169
281
        InspectionBufferSetup(det_ctx, list_id, buffer, tx->buffer, tx->buffer_len);
170
281
        InspectionBufferApplyTransforms(buffer, transforms);
171
281
    }
172
296
    return buffer;
173
296
}
174
175
/**
176
 * \brief Parse the provided function name or code to its integer
177
 *     value.
178
 *
179
 * If the value passed is a number, it will be checked that it falls
180
 * within the range of valid function codes.  If function name is
181
 * passed it will be resolved to its function code.
182
 *
183
 * \retval The function code as an integer if successful, -1 on
184
 *     failure.
185
 */
186
static int DetectDNP3FuncParseFunctionCode(const char *str, uint8_t *fc)
187
3.27k
{
188
3.27k
    if (StringParseUint8(fc, 10, (uint16_t)strlen(str), str) >= 0) {
189
723
        return 1;
190
723
    }
191
192
    /* Lookup by name. */
193
2.55k
    for (size_t i = 0;
194
90.4k
            i < sizeof(DNP3FunctionNameMap) / sizeof(DNP3Mapping); i++) {
195
89.3k
        if (strcasecmp(str, DNP3FunctionNameMap[i].name) == 0) {
196
1.41k
            *fc = (uint8_t)(DNP3FunctionNameMap[i].value);
197
1.41k
            return 1;
198
1.41k
        }
199
89.3k
    }
200
201
1.13k
    return 0;
202
2.55k
}
203
204
static int DetectDNP3FuncSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
205
86
{
206
86
    SCEnter();
207
86
    DetectDNP3 *dnp3 = NULL;
208
86
    SigMatch *sm = NULL;
209
86
    uint8_t function_code;
210
211
86
    if (DetectSignatureSetAppProto(s, ALPROTO_DNP3) != 0)
212
5
        return -1;
213
214
81
    if (!DetectDNP3FuncParseFunctionCode(str, &function_code)) {
215
23
        SCLogError("Invalid argument \"%s\" supplied to dnp3_func keyword.", str);
216
23
        return -1;
217
23
    }
218
219
58
    dnp3 = SCCalloc(1, sizeof(DetectDNP3));
220
58
    if (unlikely(dnp3 == NULL)) {
221
0
        goto error;
222
0
    }
223
58
    dnp3->function_code = function_code;
224
225
58
    sm = SigMatchAlloc();
226
58
    if (sm == NULL) {
227
0
        goto error;
228
0
    }
229
58
    sm->type = DETECT_AL_DNP3FUNC;
230
58
    sm->ctx = (void *)dnp3;
231
232
58
    SigMatchAppendSMToList(s, sm, g_dnp3_match_buffer_id);
233
234
58
    SCReturnInt(0);
235
0
error:
236
0
    if (dnp3 != NULL) {
237
0
        SCFree(dnp3);
238
0
    }
239
0
    if (sm != NULL) {
240
0
        SCFree(sm);
241
0
    }
242
0
    SCReturnInt(-1);
243
58
}
244
245
static int DetectDNP3IndParseByName(const char *str, uint16_t *flags)
246
98
{
247
98
    char tmp[strlen(str) + 1];
248
98
    char *p, *last = NULL;
249
250
98
    strlcpy(tmp, str, sizeof(tmp));
251
252
109
    for ((p = strtok_r(tmp, ",", &last)); p; (p = strtok_r(NULL, ",", &last))) {
253
96
        p = TrimString(p);
254
96
        int found = 0;
255
96
        int i = 0;
256
1.51k
        while (DNP3IndicatorsMap[i].name != NULL) {
257
1.43k
            if (strcasecmp(p, DNP3IndicatorsMap[i].name) == 0) {
258
11
                *flags |= DNP3IndicatorsMap[i].value;
259
11
                found = 1;
260
11
                break;
261
11
            }
262
1.41k
            i++;
263
1.41k
        }
264
265
96
        if (!found) {
266
85
            SCLogError("Bad argument \"%s\" supplied to dnp3.ind keyword.", p);
267
85
            return 0;
268
85
        }
269
96
    }
270
271
13
    return 1;
272
98
}
273
274
static int DetectDNP3IndParse(const char *str, uint16_t *flags)
275
247
{
276
247
    *flags = 0;
277
278
247
    if (StringParseUint16(flags, 0, (uint16_t)strlen(str), str) > 0) {
279
149
        return 1;
280
149
    }
281
282
    /* Parse by name - will log a more specific error message on error. */
283
98
    if (DetectDNP3IndParseByName(str, flags)) {
284
13
        return 1;
285
13
    }
286
287
85
    return 0;
288
98
}
289
290
static int DetectDNP3IndSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
291
29
{
292
29
    SCEnter();
293
29
    DetectDNP3 *detect = NULL;
294
29
    SigMatch *sm = NULL;
295
29
    uint16_t flags;
296
297
29
    if (DetectSignatureSetAppProto(s, ALPROTO_DNP3) != 0)
298
4
        return -1;
299
300
25
    if (!DetectDNP3IndParse(str, &flags)) {
301
19
        SCLogError("Invalid argument \"%s\" supplied to dnp3.ind keyword.", str);
302
19
        return -1;
303
19
    }
304
305
6
    detect = SCCalloc(1, sizeof(DetectDNP3));
306
6
    if (unlikely(detect == NULL)) {
307
0
        goto error;
308
0
    }
309
6
    detect->ind_flags = flags;
310
311
6
    sm = SigMatchAlloc();
312
6
    if (sm == NULL) {
313
0
        goto error;
314
0
    }
315
6
    sm->type = DETECT_AL_DNP3IND;
316
6
    sm->ctx = (void *)detect;
317
6
    SigMatchAppendSMToList(s, sm, g_dnp3_match_buffer_id);
318
319
6
    SCReturnInt(0);
320
0
error:
321
0
    if (detect != NULL) {
322
0
        SCFree(detect);
323
0
    }
324
0
    if (sm != NULL) {
325
0
        SCFree(sm);
326
0
    }
327
0
    SCReturnInt(-1);
328
6
}
329
330
/**
331
 * \brief Parse the value of string of the dnp3_obj keyword.
332
 *
333
 * \param str the input string
334
 * \param gout pointer to variable to store the parsed group integer
335
 * \param vout pointer to variable to store the parsed variation integer
336
 *
337
 * \retval 1 if parsing successful otherwise 0.
338
 */
339
static int DetectDNP3ObjParse(const char *str, uint8_t *group, uint8_t *var)
340
5.77k
{
341
5.77k
    size_t size = strlen(str) + 1;
342
5.77k
    char groupstr[size], *varstr, *sep;
343
5.77k
    strlcpy(groupstr, str, size);
344
345
5.77k
    sep = strchr(groupstr, ',');
346
5.77k
    if (sep == NULL) {
347
832
        return 0;
348
832
    }
349
4.94k
    *sep = '\0';
350
4.94k
    varstr = sep + 1;
351
352
4.94k
    if (StringParseUint8(group, 0, (uint16_t)strlen(groupstr), groupstr) < 0) {
353
1.86k
        return 0;
354
1.86k
    }
355
356
3.07k
    if (StringParseUint8(var, 0, (uint16_t)strlen(varstr), varstr) < 0) {
357
1.17k
        return 0;
358
1.17k
    }
359
360
1.89k
    return 1;
361
3.07k
}
362
363
static int DetectDNP3ObjSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
364
1.37k
{
365
1.37k
    SCEnter();
366
1.37k
    uint8_t group;
367
1.37k
    uint8_t variation;
368
1.37k
    DetectDNP3 *detect = NULL;
369
1.37k
    SigMatch *sm = NULL;
370
371
1.37k
    if (DetectSignatureSetAppProto(s, ALPROTO_DNP3) != 0)
372
16
        return -1;
373
374
1.35k
    if (!DetectDNP3ObjParse(str, &group, &variation)) {
375
1.10k
        goto fail;
376
1.10k
    }
377
378
252
    detect = SCCalloc(1, sizeof(*detect));
379
252
    if (unlikely(detect == NULL)) {
380
0
        goto fail;
381
0
    }
382
252
    detect->obj_group = group;
383
252
    detect->obj_variation = variation;
384
385
252
    sm = SigMatchAlloc();
386
252
    if (unlikely(sm == NULL)) {
387
0
        goto fail;
388
0
    }
389
252
    sm->type = DETECT_AL_DNP3OBJ;
390
252
    sm->ctx = (void *)detect;
391
252
    SigMatchAppendSMToList(s, sm, g_dnp3_match_buffer_id);
392
393
252
    SCReturnInt(1);
394
1.10k
fail:
395
1.10k
    if (detect != NULL) {
396
0
        SCFree(detect);
397
0
    }
398
1.10k
    if (sm != NULL) {
399
0
        SCFree(sm);
400
0
    }
401
1.10k
    SCReturnInt(0);
402
252
}
403
404
static void DetectDNP3Free(DetectEngineCtx *de_ctx, void *ptr)
405
4.19k
{
406
4.19k
    SCEnter();
407
4.19k
    if (ptr != NULL) {
408
4.19k
        SCFree(ptr);
409
4.19k
    }
410
4.19k
    SCReturn;
411
4.19k
}
412
413
static int DetectDNP3FuncMatch(DetectEngineThreadCtx *det_ctx,
414
    Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
415
    const SigMatchCtx *ctx)
416
9
{
417
9
    DNP3Transaction *tx = (DNP3Transaction *)txv;
418
9
    DetectDNP3 *detect = (DetectDNP3 *)ctx;
419
9
    int match = 0;
420
421
9
    if (flags & STREAM_TOSERVER && tx->is_request) {
422
5
        match = detect->function_code == tx->ah.function_code;
423
5
    } else if (flags & STREAM_TOCLIENT && !tx->is_request) {
424
4
        match = detect->function_code == tx->ah.function_code;
425
4
    }
426
427
9
    return match;
428
9
}
429
430
static int DetectDNP3ObjMatch(DetectEngineThreadCtx *det_ctx,
431
    Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
432
    const SigMatchCtx *ctx)
433
10.0k
{
434
10.0k
    DNP3Transaction *tx = (DNP3Transaction *)txv;
435
10.0k
    DetectDNP3 *detect = (DetectDNP3 *)ctx;
436
10.0k
    DNP3ObjectList *objects = NULL;
437
438
10.0k
    if (flags & STREAM_TOSERVER && tx->is_request) {
439
3.42k
        objects = &tx->objects;
440
6.63k
    } else if (flags & STREAM_TOCLIENT && !tx->is_request) {
441
6.63k
        objects = &tx->objects;
442
6.63k
    }
443
444
10.0k
    if (objects != NULL) {
445
10.0k
        DNP3Object *object;
446
10.0k
        TAILQ_FOREACH(object, objects, next) {
447
9.95k
            if (object->group == detect->obj_group &&
448
9.95k
                object->variation == detect->obj_variation) {
449
212
                return 1;
450
212
            }
451
9.95k
        }
452
10.0k
    }
453
454
9.84k
    return 0;
455
10.0k
}
456
457
static int DetectDNP3IndMatch(DetectEngineThreadCtx *det_ctx,
458
    Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
459
    const SigMatchCtx *ctx)
460
0
{
461
0
    DNP3Transaction *tx = (DNP3Transaction *)txv;
462
0
    DetectDNP3 *detect = (DetectDNP3 *)ctx;
463
464
0
    if (flags & STREAM_TOCLIENT) {
465
0
        if ((tx->iin.iin1 & (detect->ind_flags >> 8)) ||
466
0
                (tx->iin.iin2 & (detect->ind_flags & 0xf))) {
467
0
            return 1;
468
0
        }
469
0
    }
470
471
0
    return 0;
472
0
}
473
474
static void DetectDNP3FuncRegister(void)
475
73
{
476
73
    SCEnter();
477
478
73
    sigmatch_table[DETECT_AL_DNP3FUNC].name          = "dnp3_func";
479
73
    sigmatch_table[DETECT_AL_DNP3FUNC].alias         = "dnp3.func";
480
73
    sigmatch_table[DETECT_AL_DNP3FUNC].desc          = "match on the application function code found in DNP3 request and responses";
481
73
    sigmatch_table[DETECT_AL_DNP3FUNC].url           = "/rules/dnp3-keywords.html#dnp3-func";
482
73
    sigmatch_table[DETECT_AL_DNP3FUNC].Match         = NULL;
483
73
    sigmatch_table[DETECT_AL_DNP3FUNC].AppLayerTxMatch = DetectDNP3FuncMatch;
484
73
    sigmatch_table[DETECT_AL_DNP3FUNC].Setup         = DetectDNP3FuncSetup;
485
73
    sigmatch_table[DETECT_AL_DNP3FUNC].Free          = DetectDNP3Free;
486
#ifdef UNITTESTS
487
    sigmatch_table[DETECT_AL_DNP3FUNC].RegisterTests =
488
        DetectDNP3FuncRegisterTests;
489
#endif
490
73
    SCReturn;
491
73
}
492
493
static void DetectDNP3IndRegister(void)
494
73
{
495
73
    SCEnter();
496
497
73
    sigmatch_table[DETECT_AL_DNP3IND].name          = "dnp3_ind";
498
73
    sigmatch_table[DETECT_AL_DNP3IND].alias         = "dnp3.ind";
499
73
    sigmatch_table[DETECT_AL_DNP3IND].desc          = "match on the DNP3 internal indicator flags in the response application header";
500
73
    sigmatch_table[DETECT_AL_DNP3IND].url           = "/rules/dnp3-keywords.html#dnp3-ind";
501
73
    sigmatch_table[DETECT_AL_DNP3IND].Match         = NULL;
502
73
    sigmatch_table[DETECT_AL_DNP3IND].AppLayerTxMatch = DetectDNP3IndMatch;
503
73
    sigmatch_table[DETECT_AL_DNP3IND].Setup         = DetectDNP3IndSetup;
504
73
    sigmatch_table[DETECT_AL_DNP3IND].Free          = DetectDNP3Free;
505
#ifdef UNITTESTS
506
    sigmatch_table[DETECT_AL_DNP3IND].RegisterTests =
507
        DetectDNP3IndRegisterTests;
508
#endif
509
73
    SCReturn;
510
73
}
511
512
static void DetectDNP3ObjRegister(void)
513
73
{
514
73
    SCEnter();
515
516
73
    sigmatch_table[DETECT_AL_DNP3OBJ].name          = "dnp3_obj";
517
73
    sigmatch_table[DETECT_AL_DNP3OBJ].alias         = "dnp3.obj";
518
73
    sigmatch_table[DETECT_AL_DNP3OBJ].desc          = "match on the DNP3 application data objects";
519
73
    sigmatch_table[DETECT_AL_DNP3OBJ].url           = "/rules/dnp3-keywords.html#dnp3-obj";
520
73
    sigmatch_table[DETECT_AL_DNP3OBJ].Match         = NULL;
521
73
    sigmatch_table[DETECT_AL_DNP3OBJ].AppLayerTxMatch = DetectDNP3ObjMatch;
522
73
    sigmatch_table[DETECT_AL_DNP3OBJ].Setup         = DetectDNP3ObjSetup;
523
73
    sigmatch_table[DETECT_AL_DNP3OBJ].Free          = DetectDNP3Free;
524
#ifdef UNITTESTS
525
    sigmatch_table[DETECT_AL_DNP3OBJ].RegisterTests =
526
        DetectDNP3ObjRegisterTests;
527
#endif
528
73
    SCReturn;
529
73
}
530
531
static int DetectDNP3DataSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
532
3.03k
{
533
3.03k
    SCEnter();
534
3.03k
    if (DetectSignatureSetAppProto(s, ALPROTO_DNP3) != 0)
535
68
        return -1;
536
537
2.96k
    if (DetectBufferSetActiveList(de_ctx, s, g_dnp3_data_buffer_id) != 0)
538
2
        return -1;
539
540
2.96k
    SCReturnInt(0);
541
2.96k
}
542
543
static void DetectDNP3DataRegister(void)
544
73
{
545
73
    SCEnter();
546
547
73
    sigmatch_table[DETECT_AL_DNP3DATA].name          = "dnp3.data";
548
73
    sigmatch_table[DETECT_AL_DNP3DATA].alias         = "dnp3_data";
549
73
    sigmatch_table[DETECT_AL_DNP3DATA].desc          = "make the following content options to match on the re-assembled application buffer";
550
73
    sigmatch_table[DETECT_AL_DNP3DATA].url           = "/rules/dnp3-keywords.html#dnp3-data";
551
73
    sigmatch_table[DETECT_AL_DNP3DATA].Setup         = DetectDNP3DataSetup;
552
73
    sigmatch_table[DETECT_AL_DNP3DATA].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER;
553
554
73
    DetectAppLayerInspectEngineRegister2("dnp3_data",
555
73
            ALPROTO_DNP3, SIG_FLAG_TOSERVER, 0,
556
73
            DetectEngineInspectBufferGeneric,
557
73
            GetDNP3Data);
558
73
    DetectAppLayerMpmRegister2("dnp3_data", SIG_FLAG_TOSERVER, 2,
559
73
            PrefilterGenericMpmRegister, GetDNP3Data,
560
73
            ALPROTO_DNP3, 0);
561
562
73
    DetectAppLayerInspectEngineRegister2("dnp3_data",
563
73
            ALPROTO_DNP3, SIG_FLAG_TOCLIENT, 0,
564
73
            DetectEngineInspectBufferGeneric,
565
73
            GetDNP3Data);
566
73
    DetectAppLayerMpmRegister2("dnp3_data", SIG_FLAG_TOCLIENT, 2,
567
73
            PrefilterGenericMpmRegister, GetDNP3Data,
568
73
            ALPROTO_DNP3, 0);
569
570
73
    g_dnp3_data_buffer_id = DetectBufferTypeGetByName("dnp3_data");
571
73
    SCReturn;
572
73
}
573
574
void DetectDNP3Register(void)
575
73
{
576
73
    DetectDNP3DataRegister();
577
578
73
    DetectDNP3FuncRegister();
579
73
    DetectDNP3IndRegister();
580
73
    DetectDNP3ObjRegister();
581
582
    /* Register the list of func, ind and obj. */
583
73
    DetectAppLayerInspectEngineRegister2(
584
73
            "dnp3", ALPROTO_DNP3, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL);
585
73
    DetectAppLayerInspectEngineRegister2(
586
73
            "dnp3", ALPROTO_DNP3, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectGenericList, NULL);
587
588
73
    g_dnp3_match_buffer_id = DetectBufferTypeRegister("dnp3");
589
590
73
}
591
592
#ifdef UNITTESTS
593
594
#include "util-unittest.h"
595
#include "util-unittest-helper.h"
596
#include "app-layer-parser.h"
597
#include "flow-util.h"
598
#include "stream-tcp.h"
599
600
static int DetectDNP3FuncParseFunctionCodeTest(void)
601
{
602
    uint8_t fc;
603
604
    /* Valid. */
605
    FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("0", &fc));
606
    FAIL_IF(fc != 0);
607
608
    FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("1", &fc));
609
    FAIL_IF(fc != 1);
610
611
    FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("254", &fc));
612
    FAIL_IF(fc != 254);
613
614
    FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("255", &fc));
615
    FAIL_IF(fc != 255);
616
617
    FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("confirm", &fc));
618
    FAIL_IF(fc != 0);
619
620
    FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("CONFIRM", &fc));
621
    FAIL_IF(fc != 0);
622
623
    /* Invalid. */
624
    FAIL_IF(DetectDNP3FuncParseFunctionCode("", &fc));
625
    FAIL_IF(DetectDNP3FuncParseFunctionCode("-1", &fc));
626
    FAIL_IF(DetectDNP3FuncParseFunctionCode("-2", &fc));
627
    FAIL_IF(DetectDNP3FuncParseFunctionCode("256", &fc));
628
    FAIL_IF(DetectDNP3FuncParseFunctionCode("unknown_function_code", &fc));
629
630
    PASS;
631
}
632
633
static int DetectDNP3FuncTest01(void)
634
{
635
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
636
    FAIL_IF_NULL(de_ctx);
637
638
    Signature *s = DetectEngineAppendSig(de_ctx, "alert dnp3 any any -> any any "
639
                                                 "(msg:\"SURICATA DNP3 Write request\"; "
640
                                                 "dnp3_func:2; sid:5000009; rev:1;)");
641
    FAIL_IF_NULL(de_ctx->sig_list);
642
643
    SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dnp3_match_buffer_id);
644
    FAIL_IF_NULL(sm);
645
    FAIL_IF_NULL(sm->ctx);
646
647
    DetectDNP3 *dnp3func = (DetectDNP3 *)sm->ctx;
648
    FAIL_IF(dnp3func->function_code != 2);
649
650
    DetectEngineCtxFree(de_ctx);
651
    PASS;
652
}
653
654
static int DetectDNP3IndTestParseAsInteger(void)
655
{
656
    uint16_t flags = 0;
657
658
    FAIL_IF(!DetectDNP3IndParse("0", &flags));
659
    FAIL_IF(flags != 0);
660
    FAIL_IF(!DetectDNP3IndParse("1", &flags));
661
    FAIL_IF(flags != 0x0001);
662
663
    FAIL_IF(!DetectDNP3IndParse("0x0", &flags));
664
    FAIL_IF(flags != 0);
665
    FAIL_IF(!DetectDNP3IndParse("0x0000", &flags));
666
    FAIL_IF(flags != 0);
667
    FAIL_IF(!DetectDNP3IndParse("0x0001", &flags));
668
    FAIL_IF(flags != 0x0001);
669
670
    FAIL_IF(!DetectDNP3IndParse("0x8421", &flags));
671
    FAIL_IF(flags != 0x8421);
672
673
    FAIL_IF(DetectDNP3IndParse("a", &flags));
674
675
    PASS;
676
}
677
678
static int DetectDNP3IndTestParseByName(void)
679
{
680
    uint16_t flags = 0;
681
682
    FAIL_IF(!DetectDNP3IndParse("all_stations", &flags));
683
    FAIL_IF(!(flags & 0x0100));
684
    FAIL_IF(!DetectDNP3IndParse("class_1_events , class_2_events", &flags));
685
    FAIL_IF(!(flags & 0x0200));
686
    FAIL_IF(!(flags & 0x0400));
687
    FAIL_IF((flags & 0xf9ff));
688
689
    FAIL_IF(DetectDNP3IndParse("something", &flags));
690
691
    PASS;
692
}
693
694
static int DetectDNP3ObjSetupTest(void)
695
{
696
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
697
    FAIL_IF(de_ctx == NULL);
698
699
    Signature *s = DetectEngineAppendSig(de_ctx, "alert dnp3 any any -> any any "
700
                                                 "(msg:\"SURICATA DNP3 Object Test\"; "
701
                                                 "dnp3_obj:99,99; sid:1; rev:1;)");
702
    FAIL_IF(de_ctx->sig_list == NULL);
703
704
    SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dnp3_match_buffer_id);
705
    FAIL_IF_NULL(sm);
706
    FAIL_IF_NULL(sm->ctx);
707
708
    DetectDNP3 *detect = (DetectDNP3 *)sm->ctx;
709
    FAIL_IF(detect->obj_group != 99);
710
    FAIL_IF(detect->obj_variation != 99);
711
712
    DetectEngineCtxFree(de_ctx);
713
    PASS;
714
}
715
716
static int DetectDNP3ObjParseTest(void)
717
{
718
    uint8_t group, var;
719
720
    FAIL_IF(!DetectDNP3ObjParse("0,0", &group, &var));
721
    FAIL_IF(group != 0 || var != 0);
722
723
    FAIL_IF(!DetectDNP3ObjParse("255,255", &group, &var));
724
    FAIL_IF(group != 255 || var != 255);
725
726
    FAIL_IF(DetectDNP3ObjParse("-1,-1", &group, &var));
727
    FAIL_IF(DetectDNP3ObjParse("256,256", &group, &var));
728
    FAIL_IF(DetectDNP3ObjParse("a,1", &group, &var));
729
    FAIL_IF(DetectDNP3ObjParse("1,a", &group, &var));
730
731
    PASS;
732
}
733
734
static void DetectDNP3FuncRegisterTests(void)
735
{
736
    UtRegisterTest("DetectDNP3FuncParseFunctionCodeTest",
737
                   DetectDNP3FuncParseFunctionCodeTest);
738
    UtRegisterTest("DetectDNP3FuncTest01", DetectDNP3FuncTest01);
739
}
740
741
static void DetectDNP3IndRegisterTests(void)
742
{
743
    UtRegisterTest("DetectDNP3IndTestParseAsInteger",
744
                   DetectDNP3IndTestParseAsInteger);
745
    UtRegisterTest("DetectDNP3IndTestParseByName",
746
                   DetectDNP3IndTestParseByName);
747
}
748
749
static void DetectDNP3ObjRegisterTests(void)
750
{
751
    UtRegisterTest("DetectDNP3ObjParseTest", DetectDNP3ObjParseTest);
752
    UtRegisterTest("DetectDNP3ObjSetupTest", DetectDNP3ObjSetupTest);
753
}
754
#endif