/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 |