• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.18
/src/detect-dnp3.c
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-buffer.h"
27
#include "detect-engine-mpm.h"
28
#include "detect-engine-prefilter.h"
29
#include "detect-engine-content-inspection.h"
30
#include "detect-engine-uint.h"
31

32
#include "app-layer-dnp3.h"
33
#include "util-byte.h"
34

35
static int g_dnp3_match_buffer_id = 0;
36
static int g_dnp3_data_buffer_id = 0;
37
static int g_dnp3_ind_buffer_id = 0;
38

39
/**
40
 * The detection struct.
41
 */
42
typedef struct DetectDNP3_ {
43
    /* Object info for object detection. */
44
    uint8_t obj_group;
45
    uint8_t obj_variation;
46
} DetectDNP3;
47

48
#ifdef UNITTESTS
49
static void DetectDNP3FuncRegisterTests(void);
50
static void DetectDNP3ObjRegisterTests(void);
51
#endif
52

53
static InspectionBuffer *GetDNP3Data(DetectEngineThreadCtx *det_ctx,
54
        const DetectEngineTransforms *transforms,
55
        Flow *_f, const uint8_t flow_flags,
56
        void *txv, const int list_id)
57
{
583✔
58
    SCLogDebug("list_id %d", list_id);
583✔
59
    InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
583✔
60
    if (buffer->inspect == NULL) {
583✔
61
        DNP3Transaction *tx = (DNP3Transaction *)txv;
545✔
62
        SCLogDebug("tx %p", tx);
545✔
63

64
        if ((flow_flags & STREAM_TOSERVER && !tx->is_request) ||
545✔
65
                (flow_flags & STREAM_TOCLIENT && tx->is_request)) {
545✔
66
            return NULL;
×
67
        }
×
68

69
        if (tx->buffer == NULL || tx->buffer_len == 0) {
545✔
70
            return NULL;
×
71
        }
×
72

73
        SCLogDebug("tx %p data %p data_len %u", tx, tx->buffer, tx->buffer_len);
545✔
74
        InspectionBufferSetupAndApplyTransforms(
545✔
75
                det_ctx, list_id, buffer, tx->buffer, tx->buffer_len, transforms);
545✔
76
    }
545✔
77
    return buffer;
583✔
78
}
583✔
79

80
static void DetectDNP3FuncFree(DetectEngineCtx *de_ctx, void *ptr)
81
{
2,146✔
82
    SCDetectU8Free(ptr);
2,146✔
83
}
2,146✔
84

85
static int DetectDNP3FuncSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
86
{
3,384✔
87
    SCEnter();
3,384✔
88

89
    if (SCDetectSignatureSetAppProto(s, ALPROTO_DNP3) != 0)
3,384✔
90
        return -1;
82✔
91

92
    DetectU8Data *detect = SCDnp3DetectFuncParse(str);
3,302✔
93
    if (detect == NULL) {
3,302✔
94
        SCLogError("Invalid argument \"%s\" supplied to dnp3_func keyword.", str);
1,156✔
95
        return -1;
1,156✔
96
    }
1,156✔
97

98
    if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_DNP3FUNC, (SigMatchCtx *)detect,
2,146✔
99
                g_dnp3_match_buffer_id) == NULL) {
2,146✔
100
        goto error;
×
101
    }
×
102

103
    SCReturnInt(0);
2,146✔
104
error:
×
105
    if (detect != NULL) {
×
106
        DetectDNP3FuncFree(NULL, detect);
×
107
    }
×
108
    SCReturnInt(-1);
×
109
}
2,146✔
110

111
static void DetectDNP3IndFree(DetectEngineCtx *de_ctx, void *ptr)
112
{
163✔
113
    SCDetectU16Free(ptr);
163✔
114
}
163✔
115

116
static int DetectDNP3IndSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
117
{
216✔
118
    SCEnter();
216✔
119

120
    if (SCDetectSignatureSetAppProto(s, ALPROTO_DNP3) != 0)
216✔
121
        return -1;
1✔
122

123
    DetectU16Data *detect = SCDnp3DetectIndParse(str);
215✔
124
    if (detect == NULL) {
215✔
125
        SCLogError("Invalid argument \"%s\" supplied to dnp3.ind keyword.", str);
52✔
126
        return -1;
52✔
127
    }
52✔
128

129
    if (SCSigMatchAppendSMToList(
163✔
130
                de_ctx, s, DETECT_DNP3IND, (SigMatchCtx *)detect, g_dnp3_ind_buffer_id) == NULL) {
163✔
131
        goto error;
×
132
    }
×
133

134
    SCReturnInt(0);
163✔
135
error:
×
136
    if (detect != NULL) {
×
137
        DetectDNP3IndFree(NULL, detect);
×
138
    }
×
139
    SCReturnInt(-1);
×
140
}
163✔
141

142
/**
143
 * \brief Parse the value of string of the dnp3_obj keyword.
144
 *
145
 * \param str the input string
146
 * \param gout pointer to variable to store the parsed group integer
147
 * \param vout pointer to variable to store the parsed variation integer
148
 *
149
 * \retval 1 if parsing successful otherwise 0.
150
 */
151
static int DetectDNP3ObjParse(const char *str, uint8_t *group, uint8_t *var)
152
{
4,069✔
153
    size_t size = strlen(str) + 1;
4,069✔
154
    char groupstr[size], *varstr, *sep;
4,069✔
155
    strlcpy(groupstr, str, size);
4,069✔
156

157
    sep = strchr(groupstr, ',');
4,069✔
158
    if (sep == NULL) {
4,069✔
159
        return 0;
897✔
160
    }
897✔
161
    *sep = '\0';
3,172✔
162
    varstr = sep + 1;
3,172✔
163

164
    if (StringParseUint8(group, 0, (uint16_t)strlen(groupstr), groupstr) <= 0) {
3,172✔
165
        return 0;
1,004✔
166
    }
1,004✔
167

168
    if (StringParseUint8(var, 0, (uint16_t)strlen(varstr), varstr) <= 0) {
2,168✔
169
        return 0;
790✔
170
    }
790✔
171

172
    return 1;
1,378✔
173
}
2,168✔
174

175
static int DetectDNP3ObjSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
176
{
4,264✔
177
    SCEnter();
4,264✔
178
    uint8_t group;
4,264✔
179
    uint8_t variation;
4,264✔
180
    DetectDNP3 *detect = NULL;
4,264✔
181

182
    if (SCDetectSignatureSetAppProto(s, ALPROTO_DNP3) != 0)
4,264✔
183
        return -1;
195✔
184

185
    if (!DetectDNP3ObjParse(str, &group, &variation)) {
4,069✔
186
        goto fail;
2,691✔
187
    }
2,691✔
188

189
    detect = SCCalloc(1, sizeof(*detect));
1,378✔
190
    if (unlikely(detect == NULL)) {
1,378✔
191
        goto fail;
×
192
    }
×
193
    detect->obj_group = group;
1,378✔
194
    detect->obj_variation = variation;
1,378✔
195

196
    if (SCSigMatchAppendSMToList(
1,378✔
197
                de_ctx, s, DETECT_DNP3OBJ, (SigMatchCtx *)detect, g_dnp3_match_buffer_id) == NULL) {
1,378✔
198
        goto fail;
×
199
    }
×
200

201
    SCReturnInt(1);
1,378✔
202
fail:
2,691✔
203
    if (detect != NULL) {
2,691✔
204
        SCFree(detect);
×
205
    }
×
206
    SCReturnInt(0);
2,691✔
207
}
1,378✔
208

209
static void DetectDNP3Free(DetectEngineCtx *de_ctx, void *ptr)
210
{
1,378✔
211
    SCEnter();
1,378✔
212
    if (ptr != NULL) {
1,378✔
213
        SCFree(ptr);
1,378✔
214
    }
1,378✔
215
    SCReturn;
1,378✔
216
}
1,378✔
217

218
static int DetectDNP3FuncMatch(DetectEngineThreadCtx *det_ctx,
219
    Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
220
    const SigMatchCtx *ctx)
221
{
42✔
222
    DNP3Transaction *tx = (DNP3Transaction *)txv;
42✔
223
    DetectU8Data *detect = (DetectU8Data *)ctx;
42✔
224

225
    if (flags & STREAM_TOSERVER && tx->is_request) {
42✔
226
        return DetectU8Match(tx->ah.function_code, detect);
18✔
227
    } else if (flags & STREAM_TOCLIENT && !tx->is_request) {
24✔
228
        return DetectU8Match(tx->ah.function_code, detect);
24✔
229
    }
24✔
230

231
    return 0;
×
232
}
42✔
233

234
static int DetectDNP3ObjMatch(DetectEngineThreadCtx *det_ctx,
235
    Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
236
    const SigMatchCtx *ctx)
237
{
337✔
238
    DNP3Transaction *tx = (DNP3Transaction *)txv;
337✔
239
    DetectDNP3 *detect = (DetectDNP3 *)ctx;
337✔
240
    DNP3ObjectList *objects = NULL;
337✔
241

242
    if (flags & STREAM_TOSERVER && tx->is_request) {
337✔
243
        objects = &tx->objects;
159✔
244
    } else if (flags & STREAM_TOCLIENT && !tx->is_request) {
178✔
245
        objects = &tx->objects;
178✔
246
    }
178✔
247

248
    if (objects != NULL) {
337✔
249
        DNP3Object *object;
337✔
250
        TAILQ_FOREACH(object, objects, next) {
345✔
251
            if (object->group == detect->obj_group &&
345✔
252
                object->variation == detect->obj_variation) {
345✔
253
                return 1;
3✔
254
            }
3✔
255
        }
345✔
256
    }
337✔
257

258
    return 0;
334✔
259
}
337✔
260

261
static int DetectDNP3IndMatch(DetectEngineThreadCtx *det_ctx,
262
    Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
263
    const SigMatchCtx *ctx)
UNCOV
264
{
×
UNCOV
265
    DNP3Transaction *tx = (DNP3Transaction *)txv;
×
UNCOV
266
    DetectU16Data *detect = (DetectU16Data *)ctx;
×
267

UNCOV
268
    return DetectU16Match((uint16_t)((tx->iin.iin1 << 8) | tx->iin.iin2), detect);
×
UNCOV
269
}
×
270

271
static void DetectDNP3FuncRegister(void)
272
{
3✔
273
    SCEnter();
3✔
274

275
    sigmatch_table[DETECT_DNP3FUNC].name = "dnp3_func";
3✔
276
    sigmatch_table[DETECT_DNP3FUNC].alias = "dnp3.func";
3✔
277
    sigmatch_table[DETECT_DNP3FUNC].desc =
3✔
278
            "match on the application function code found in DNP3 request and responses";
3✔
279
    sigmatch_table[DETECT_DNP3FUNC].url = "/rules/dnp3-keywords.html#dnp3-func";
3✔
280
    sigmatch_table[DETECT_DNP3FUNC].Match = NULL;
3✔
281
    sigmatch_table[DETECT_DNP3FUNC].AppLayerTxMatch = DetectDNP3FuncMatch;
3✔
282
    sigmatch_table[DETECT_DNP3FUNC].Setup = DetectDNP3FuncSetup;
3✔
283
    sigmatch_table[DETECT_DNP3FUNC].Free = DetectDNP3FuncFree;
3✔
284
    sigmatch_table[DETECT_DNP3FUNC].flags = SIGMATCH_INFO_UINT8 | SIGMATCH_INFO_ENUM_UINT;
3✔
285
#ifdef UNITTESTS
286
    sigmatch_table[DETECT_DNP3FUNC].RegisterTests = DetectDNP3FuncRegisterTests;
287
#endif
288
    SCReturn;
3✔
289
}
3✔
290

291
static void DetectDNP3IndRegister(void)
292
{
3✔
293
    SCEnter();
3✔
294

295
    sigmatch_table[DETECT_DNP3IND].name = "dnp3_ind";
3✔
296
    sigmatch_table[DETECT_DNP3IND].alias = "dnp3.ind";
3✔
297
    sigmatch_table[DETECT_DNP3IND].desc =
3✔
298
            "match on the DNP3 internal indicator flags in the response application header";
3✔
299
    sigmatch_table[DETECT_DNP3IND].url = "/rules/dnp3-keywords.html#dnp3-ind";
3✔
300
    sigmatch_table[DETECT_DNP3IND].Match = NULL;
3✔
301
    sigmatch_table[DETECT_DNP3IND].AppLayerTxMatch = DetectDNP3IndMatch;
3✔
302
    sigmatch_table[DETECT_DNP3IND].Setup = DetectDNP3IndSetup;
3✔
303
    sigmatch_table[DETECT_DNP3IND].Free = DetectDNP3IndFree;
3✔
304
    sigmatch_table[DETECT_DNP3IND].flags = SIGMATCH_INFO_UINT16 | SIGMATCH_INFO_BITFLAGS_UINT;
3✔
305
    SCReturn;
3✔
306
}
3✔
307

308
static void DetectDNP3ObjRegister(void)
309
{
3✔
310
    SCEnter();
3✔
311

312
    sigmatch_table[DETECT_DNP3OBJ].name = "dnp3_obj";
3✔
313
    sigmatch_table[DETECT_DNP3OBJ].alias = "dnp3.obj";
3✔
314
    sigmatch_table[DETECT_DNP3OBJ].desc = "match on the DNP3 application data objects";
3✔
315
    sigmatch_table[DETECT_DNP3OBJ].url = "/rules/dnp3-keywords.html#dnp3-obj";
3✔
316
    sigmatch_table[DETECT_DNP3OBJ].Match = NULL;
3✔
317
    sigmatch_table[DETECT_DNP3OBJ].AppLayerTxMatch = DetectDNP3ObjMatch;
3✔
318
    sigmatch_table[DETECT_DNP3OBJ].Setup = DetectDNP3ObjSetup;
3✔
319
    sigmatch_table[DETECT_DNP3OBJ].Free = DetectDNP3Free;
3✔
320
#ifdef UNITTESTS
321
    sigmatch_table[DETECT_DNP3OBJ].RegisterTests = DetectDNP3ObjRegisterTests;
322
#endif
323
    SCReturn;
3✔
324
}
3✔
325

326
static int DetectDNP3DataSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
327
{
2,813✔
328
    SCEnter();
2,813✔
329
    if (SCDetectSignatureSetAppProto(s, ALPROTO_DNP3) != 0)
2,813✔
330
        return -1;
42✔
331

332
    if (SCDetectBufferSetActiveList(de_ctx, s, g_dnp3_data_buffer_id) != 0)
2,771✔
333
        return -1;
2✔
334

335
    SCReturnInt(0);
2,771✔
336
}
2,771✔
337

338
static void DetectDNP3DataRegister(void)
339
{
3✔
340
    SCEnter();
3✔
341

342
    sigmatch_table[DETECT_DNP3DATA].name = "dnp3.data";
3✔
343
    sigmatch_table[DETECT_DNP3DATA].alias = "dnp3_data";
3✔
344
    sigmatch_table[DETECT_DNP3DATA].desc =
3✔
345
            "make the following content options to match on the re-assembled application buffer";
3✔
346
    sigmatch_table[DETECT_DNP3DATA].url = "/rules/dnp3-keywords.html#dnp3-data";
3✔
347
    sigmatch_table[DETECT_DNP3DATA].Setup = DetectDNP3DataSetup;
3✔
348
    sigmatch_table[DETECT_DNP3DATA].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
3✔
349

350
    DetectAppLayerInspectEngineRegister("dnp3_data", ALPROTO_DNP3, SIG_FLAG_TOSERVER, 0,
3✔
351
            DetectEngineInspectBufferGeneric, GetDNP3Data);
3✔
352
    DetectAppLayerMpmRegister("dnp3_data", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister,
3✔
353
            GetDNP3Data, ALPROTO_DNP3, 0);
3✔
354

355
    DetectAppLayerInspectEngineRegister("dnp3_data", ALPROTO_DNP3, SIG_FLAG_TOCLIENT, 0,
3✔
356
            DetectEngineInspectBufferGeneric, GetDNP3Data);
3✔
357
    DetectAppLayerMpmRegister("dnp3_data", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister,
3✔
358
            GetDNP3Data, ALPROTO_DNP3, 0);
3✔
359

360
    g_dnp3_data_buffer_id = DetectBufferTypeGetByName("dnp3_data");
3✔
361
    SCReturn;
3✔
362
}
3✔
363

364
void DetectDNP3Register(void)
365
{
3✔
366
    DetectDNP3DataRegister();
3✔
367

368
    DetectDNP3FuncRegister();
3✔
369
    DetectDNP3IndRegister();
3✔
370
    DetectDNP3ObjRegister();
3✔
371

372
    /* Register the list of func, ind and obj. */
373
    DetectAppLayerInspectEngineRegister(
3✔
374
            "dnp3", ALPROTO_DNP3, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL);
3✔
375
    DetectAppLayerInspectEngineRegister(
3✔
376
            "dnp3", ALPROTO_DNP3, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectGenericList, NULL);
3✔
377

378
    g_dnp3_match_buffer_id = DetectBufferTypeRegister("dnp3");
3✔
379

380
    DetectAppLayerInspectEngineRegister(
3✔
381
            "dnp3_ind", ALPROTO_DNP3, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectGenericList, NULL);
3✔
382
    g_dnp3_ind_buffer_id = DetectBufferTypeRegister("dnp3_ind");
3✔
383
}
3✔
384

385
#ifdef UNITTESTS
386

387
#include "util-unittest.h"
388
#include "util-unittest-helper.h"
389
#include "app-layer-parser.h"
390
#include "flow-util.h"
391
#include "stream-tcp.h"
392

393
static int DetectDNP3FuncTest01(void)
394
{
395
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
396
    FAIL_IF_NULL(de_ctx);
397

398
    Signature *s = DetectEngineAppendSig(de_ctx, "alert dnp3 any any -> any any "
399
                                                 "(msg:\"SURICATA DNP3 Write request\"; "
400
                                                 "dnp3_func:2; sid:5000009; rev:1;)");
401
    FAIL_IF_NULL(de_ctx->sig_list);
402

403
    SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dnp3_match_buffer_id);
404
    FAIL_IF_NULL(sm);
405
    FAIL_IF_NULL(sm->ctx);
406

407
    DetectU8Data *dnp3func = (DetectU8Data *)sm->ctx;
408
    FAIL_IF(dnp3func->arg1 != 2);
409

410
    DetectEngineCtxFree(de_ctx);
411
    PASS;
412
}
413

414
static int DetectDNP3ObjSetupTest(void)
415
{
416
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
417
    FAIL_IF(de_ctx == NULL);
418

419
    Signature *s = DetectEngineAppendSig(de_ctx, "alert dnp3 any any -> any any "
420
                                                 "(msg:\"SURICATA DNP3 Object Test\"; "
421
                                                 "dnp3_obj:99,99; sid:1; rev:1;)");
422
    FAIL_IF(de_ctx->sig_list == NULL);
423

424
    SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dnp3_match_buffer_id);
425
    FAIL_IF_NULL(sm);
426
    FAIL_IF_NULL(sm->ctx);
427

428
    DetectDNP3 *detect = (DetectDNP3 *)sm->ctx;
429
    FAIL_IF(detect->obj_group != 99);
430
    FAIL_IF(detect->obj_variation != 99);
431

432
    DetectEngineCtxFree(de_ctx);
433
    PASS;
434
}
435

436
static int DetectDNP3ObjParseTest(void)
437
{
438
    uint8_t group, var;
439

440
    FAIL_IF(!DetectDNP3ObjParse("0,0", &group, &var));
441
    FAIL_IF(group != 0 || var != 0);
442

443
    FAIL_IF(!DetectDNP3ObjParse("255,255", &group, &var));
444
    FAIL_IF(group != 255 || var != 255);
445

446
    FAIL_IF(DetectDNP3ObjParse("-1,-1", &group, &var));
447
    FAIL_IF(DetectDNP3ObjParse("256,256", &group, &var));
448
    FAIL_IF(DetectDNP3ObjParse("a,1", &group, &var));
449
    FAIL_IF(DetectDNP3ObjParse("1,a", &group, &var));
450

451
    PASS;
452
}
453

454
static void DetectDNP3FuncRegisterTests(void)
455
{
456
    UtRegisterTest("DetectDNP3FuncTest01", DetectDNP3FuncTest01);
457
}
458

459
static void DetectDNP3ObjRegisterTests(void)
460
{
461
    UtRegisterTest("DetectDNP3ObjParseTest", DetectDNP3ObjParseTest);
462
    UtRegisterTest("DetectDNP3ObjSetupTest", DetectDNP3ObjSetupTest);
463
}
464
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc