1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License Version 2 as
4 * published by the Free Software Foundation. You may not use, modify or
5 * distribute this program under any other version of the GNU General
6 * Public License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 *
17 * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
18 * Copyright (C) 2011-2013 Sourcefire, Inc.
19 *
20 * Author: Ryan Jordan
21 *
22 * Rule options for the DNP3 preprocessor
23 *
24 */
25
26 #include <string.h>
27
28 #include "sf_types.h"
29 #include "sf_snort_plugin_api.h"
30 #include "sf_snort_packet.h"
31 #include "sf_dynamic_preprocessor.h"
32 #include "mempool.h"
33
34 #include "spp_dnp3.h"
35 #include "dnp3_map.h"
36 #include "dnp3_roptions.h"
37
38 /* Object decoding constants */
39 #define DNP3_OBJ_HDR_MIN_LEN 3 /* group, var, qualifier */
40 #define DNP3_OBJ_QUAL_PREFIX(x) ((x & 0x70) >> 4)
41 #define DNP3_OBJ_QUAL_RANGE(x) (x & 0x0F)
42
43 /* Object header prefix codes */
44 #define DNP3_PREFIX_NO_PREFIX 0x00
45 #define DNP3_PREFIX_1OCT_INDEX 0x01
46 #define DNP3_PREFIX_2OCT_INDEX 0x02
47 #define DNP3_PREFIX_4OCT_INDEX 0x03
48 #define DNP3_PREFIX_1OCT_SIZE 0x04
49 #define DNP3_PREFIX_2OCT_SIZE 0x05
50 #define DNP3_PREFIX_4OCT_SIZE 0x06
51 #define DNP3_PREFIX_RESERVED 0x07
52
53 /* Object header range specifiers -- 0x0A & 0x0C-0x0F are reserved */
54 #define DNP3_RANGE_1OCT_INDICES 0x00
55 #define DNP3_RANGE_2OCT_INDICES 0x01
56 #define DNP3_RANGE_4OCT_INDICES 0x02
57 #define DNP3_RANGE_1OCT_ADDRESSES 0x03
58 #define DNP3_RANGE_2OCT_ADDRESSES 0x04
59 #define DNP3_RANGE_4OCT_ADDRESSES 0x05
60 #define DNP3_RANGE_NO_RANGE 0x06
61 #define DNP3_RANGE_1OCT_COUNT 0x07
62 #define DNP3_RANGE_2OCT_COUNT 0x08
63 #define DNP3_RANGE_4OCT_COUNT 0x09
64 #define DNP3_RANGE_VARIABLE 0x0B
65
66 typedef enum _dnp3_option_type_t
67 {
68 DNP3_FUNC = 0,
69 DNP3_OBJ,
70 DNP3_IND,
71 DNP3_DATA
72 } dnp3_option_type_t;
73
74 typedef struct _dnp3_option_data_t
75 {
76 dnp3_option_type_t type;
77 uint16_t arg;
78 } dnp3_option_data_t;
79
80 /* Parsing functions */
DNP3FuncInit(struct _SnortConfig * sc,char * name,char * params,void ** data)81 int DNP3FuncInit(struct _SnortConfig *sc, char *name, char *params, void **data)
82 {
83 char *endptr;
84 dnp3_option_data_t *dnp3_data;
85 long func_code;
86
87 if (name == NULL || data == NULL)
88 return 0;
89
90 if (params == NULL)
91 {
92 DynamicPreprocessorFatalMessage("%s(%d): dnp3_func requires a "
93 "number beween 0 and 255, or a valid function name.\n",
94 *_dpd.config_file, *_dpd.config_line);
95 }
96
97 if (strcmp(name, DNP3_FUNC_NAME) != 0)
98 return 0;
99
100 dnp3_data = (dnp3_option_data_t *)calloc(1, sizeof(dnp3_option_data_t));
101 if (dnp3_data == NULL)
102 {
103 DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for "
104 "dnp3_func data structure.\n", __FILE__, __LINE__);
105 }
106
107 /* Parsing time */
108 if (isdigit(params[0]))
109 {
110 /* Function code given as integer */
111 func_code = _dpd.SnortStrtol(params, &endptr, 10);
112 if ((func_code > 255) || (func_code < 0) || (*endptr != '\0'))
113 {
114 DynamicPreprocessorFatalMessage("%s(%d): dnp3_func requires a "
115 "number beween 0 and 255, or a valid function name.\n",
116 *_dpd.config_file, *_dpd.config_line);
117 }
118 }
119 else
120 {
121 func_code = DNP3FuncStrToCode(params);
122
123 if (func_code == -1)
124 {
125 DynamicPreprocessorFatalMessage("%s(%d): dnp3_func requires a "
126 "number beween 0 and 255, or a valid function name.\n",
127 *_dpd.config_file, *_dpd.config_line);
128 }
129 }
130
131 dnp3_data->type = DNP3_FUNC;
132 dnp3_data->arg = (uint16_t) func_code;
133
134 *data = (void *)dnp3_data;
135
136 return 1;
137 }
138
DNP3ObjError(void)139 NORETURN static inline void DNP3ObjError(void)
140 {
141 DynamicPreprocessorFatalMessage("%s(%d) dnp3_obj requires two arguments,"
142 "where each argument is a number between 0 and 255.\n",
143 *_dpd.config_file, *_dpd.config_line);
144 }
145
DNP3ObjInit(struct _SnortConfig * sc,char * name,char * params,void ** data)146 int DNP3ObjInit(struct _SnortConfig *sc, char *name, char *params, void **data)
147 {
148 char *endptr, *token, *saveptr;
149 dnp3_option_data_t *dnp3_data;
150 unsigned int obj_group, obj_var;
151
152 if (name == NULL || data == NULL)
153 return 0;
154
155 if (strcmp(name, DNP3_OBJ_NAME) != 0)
156 return 0;
157
158 if (params == NULL)
159 {
160 DynamicPreprocessorFatalMessage("%s(%d): No argument given for dnp3_obj. "
161 "dnp3_obj requires two arguments, where each argument is a number "
162 "between 0 and 255.\n", *_dpd.config_file, *_dpd.config_line);
163 }
164
165 dnp3_data = (dnp3_option_data_t *)calloc(1, sizeof(dnp3_option_data_t));
166 if (dnp3_data == NULL)
167 {
168 DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for "
169 "dnp3_func data structure.\n", __FILE__, __LINE__);
170 }
171
172 token = strtok_r(params, ",", &saveptr);
173 if (token == NULL)
174 DNP3ObjError();
175
176 /* First token: object group */
177 obj_group = _dpd.SnortStrtoul(token, &endptr, 10);
178 if ((obj_group > 255) || (*endptr != '\0'))
179 DNP3ObjError();
180
181 token = strtok_r(NULL, ",", &saveptr);
182 if (token == NULL)
183 DNP3ObjError();
184
185 /* Second token: object var */
186 obj_var = _dpd.SnortStrtoul(token, &endptr, 10);
187 if ((obj_var > 255) || (*endptr != '\0'))
188 DNP3ObjError();
189
190 /* pack the two arguments into one uint16_t */
191 dnp3_data->type = DNP3_OBJ;
192 dnp3_data->arg = ((obj_group << 8) | (obj_var));
193
194 *data = dnp3_data;
195
196 return 1;
197 }
198
DNP3IndInit(struct _SnortConfig * sc,char * name,char * params,void ** data)199 int DNP3IndInit(struct _SnortConfig *sc, char *name, char *params, void **data)
200 {
201 dnp3_option_data_t *dnp3_data;
202 char *token, *saveptr;
203 uint16_t flags = 0;
204
205 if (name == NULL || data == NULL)
206 return 0;
207
208 if (params == NULL)
209 {
210 DynamicPreprocessorFatalMessage("%s(%d): dnp3_ind requires a "
211 "number beween 0 and 255, or a valid function name.\n",
212 *_dpd.config_file, *_dpd.config_line);
213 }
214
215 dnp3_data = (dnp3_option_data_t *)calloc(1, sizeof(dnp3_option_data_t));
216 if (dnp3_data == NULL)
217 {
218 DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for "
219 "dnp3_func data structure.\n", __FILE__, __LINE__);
220 }
221
222 token = strtok_r(params, ",", &saveptr);
223
224 while (token != NULL)
225 {
226 int flag = DNP3IndStrToCode(token);
227
228 if (flag == -1)
229 {
230 DynamicPreprocessorFatalMessage("%s(%d): dnp3_ind requires a "
231 "valid indication flag name. '%s' is invalid.\n",
232 *_dpd.config_file, *_dpd.config_line, token);
233 }
234
235 flags |= (uint16_t) flag;
236 token = strtok_r(NULL, ",", &saveptr);
237 }
238
239 if (flags == 0)
240 {
241 DynamicPreprocessorFatalMessage("%s(%d): dnp3_ind requires a "
242 "valid indication flag name. No flags were given.\n",
243 *_dpd.config_file, *_dpd.config_line);
244 }
245
246 dnp3_data->type = DNP3_IND;
247 dnp3_data->arg = flags;
248
249 *data = (void *)dnp3_data;
250
251 return 1;
252 }
253
DNP3DataInit(struct _SnortConfig * sc,char * name,char * params,void ** data)254 int DNP3DataInit(struct _SnortConfig *sc, char *name, char *params, void **data)
255 {
256 dnp3_option_data_t *dnp3_data;
257
258 if (name == NULL || data == NULL)
259 return 0;
260
261 /* nothing to parse. */
262 if (params)
263 {
264 DynamicPreprocessorFatalMessage("%s(%d): dnp3_data does not take "
265 "any arguments.\n", *_dpd.config_file, *_dpd.config_line);
266 }
267
268 dnp3_data = (dnp3_option_data_t *)calloc(1, sizeof(dnp3_option_data_t));
269 if (dnp3_data == NULL)
270 {
271 DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for "
272 "dnp3_data data structure.\n", __FILE__, __LINE__);
273 }
274
275 dnp3_data->type = DNP3_DATA;
276 dnp3_data->arg = 0;
277
278 *data = (void *)dnp3_data;
279
280 return 1;
281 }
282
283 /* Evaluation functions */
DNP3FuncEval(void * raw_packet,const uint8_t ** cursor,void * data)284 int DNP3FuncEval(void *raw_packet, const uint8_t **cursor, void *data)
285 {
286 SFSnortPacket *packet = (SFSnortPacket *)raw_packet;
287 MemBucket *tmp_bucket;
288 dnp3_option_data_t *rule_data = (dnp3_option_data_t *)data;
289 dnp3_session_data_t *session_data;
290 dnp3_reassembly_data_t *rdata;
291
292 /* The preprocessor only evaluates PAF-flushed PDUs. If the rule options
293 don't check for this, they'll fire on stale session data when the
294 original packet goes through before flushing. */
295 if (packet->tcp_header && !PacketHasFullPDU(packet))
296 return RULE_NOMATCH;
297
298 /* For UDP packets, there is no PAF so we use the Alt Decode buffer. */
299 if (packet->udp_header && !_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE))
300 return RULE_NOMATCH;
301
302 tmp_bucket = (MemBucket *)
303 _dpd.sessionAPI->get_application_data(packet->stream_session, PP_DNP3);
304
305 if ((packet->payload_size == 0) || (tmp_bucket == NULL))
306 {
307 return RULE_NOMATCH;
308 }
309
310 session_data = (dnp3_session_data_t *)tmp_bucket->data;
311
312 if (session_data->direction == DNP3_CLIENT)
313 rdata = &(session_data->client_rdata);
314 else
315 rdata = &(session_data->server_rdata);
316
317 /* Only evaluate rules against complete Application-layer fragments */
318 if (rdata->state != DNP3_REASSEMBLY_STATE__DONE)
319 return RULE_NOMATCH;
320
321 if (session_data->func == rule_data->arg)
322 return RULE_MATCH;
323
324 return RULE_NOMATCH;
325 }
326
DNP3DecodeObject(uint8_t * buf,uint16_t buflen,uint8_t rule_group,uint8_t rule_var)327 static int DNP3DecodeObject(uint8_t *buf, uint16_t buflen, uint8_t rule_group, uint8_t rule_var)
328 {
329 uint8_t group, var;
330
331 /* XXX: uncomment these when fixing the below TODO regarding multiple objects
332 uint8_t qualifier, prefix_size, prefix_code, range_specifier;
333 uint32_t begin, end, num_objects;
334 */
335
336 if (buf == NULL || buflen < DNP3_OBJ_HDR_MIN_LEN)
337 return RULE_NOMATCH;
338
339 /* Decode group */
340 group = *buf;
341 buf++;
342 buflen--;
343
344 /* Decode var */
345 var = *buf;
346 buf++;
347 buflen--;
348
349 /* Match the rule option here, quit decoding if we found the right header. */
350 if ((group == rule_group) && (var == rule_var))
351 return RULE_MATCH;
352
353 /* TODO: Implement matching with multiple objects in a Request/Response. */
354 #if 0
355 /* Decode qualifier */
356 qualifier = *buf;
357 prefix_code = DNP3_OBJ_QUAL_PREFIX(qualifier);
358 range_specifier = DNP3_OBJ_QUAL_RANGE(qualifier);
359 buf++;
360 buflen--;
361
362 /* The size of object prefixes depends on the prefix code */
363 switch (prefix_code)
364 {
365 case DNP3_PREFIX_NO_PREFIX:
366 prefix_size = 0;
367 break;
368
369 case DNP3_PREFIX_1OCT_INDEX:
370 case DNP3_PREFIX_1OCT_SIZE:
371 prefix_size = 1;
372 break;
373
374 case DNP3_PREFIX_2OCT_INDEX:
375 case DNP3_PREFIX_2OCT_SIZE:
376 prefix_size = 2;
377 break;
378
379 case DNP3_PREFIX_4OCT_INDEX:
380 case DNP3_PREFIX_4OCT_SIZE:
381 prefix_size = 4;
382 break;
383
384 default:
385 /* TODO: Preprocessor alert on reserved value */
386 return DNP3_FAIL;
387 }
388
389 /* Decoding of the range field depends on the Range Specifier */
390 switch (range_specifier)
391 {
392 case DNP3_RANGE_1OCT_INDICES:
393 if (buflen < 2)
394 return DNP3_FAIL;
395
396 /* Decode 8-bit indices for object prefixes */
397 begin = *(uint8_t *)buf++;
398 end = *(uint8_t *)buf++;
399 buflen -= 2;
400
401 /* Check that indices make sense */
402 if (begin > end)
403 return DNP3_FAIL; /* TODO: Preprocessor alert */
404
405 num_objects = end - begin + 1;
406 break;
407
408 case DNP3_RANGE_2OCT_INDICES:
409 if (buflen < 2)
410 return DNP3_FAIL;
411
412 /* Decode 8-bit indices for object prefixes */
413 begin = *(uint16_t *)buf++;
414 end = *(uint16_t *)buf++;
415 buflen -= 2;
416
417 /* Check that indices make sense */
418 if (begin > end)
419 return DNP3_FAIL; /* TODO: Preprocessor alert */
420
421 num_objects = end - begin + 1;
422 break;
423
424 case DNP3_RANGE_4OCT_INDICES:
425 case DNP3_RANGE_1OCT_ADDRESSES:
426 case DNP3_RANGE_2OCT_ADDRESSES:
427 case DNP3_RANGE_4OCT_ADDRESSES:
428 case DNP3_RANGE_NO_RANGE:
429 case DNP3_RANGE_1OCT_COUNT:
430 case DNP3_RANGE_2OCT_COUNT:
431 case DNP3_RANGE_4OCT_COUNT:
432 case DNP3_RANGE_VARIABLE:
433 default:
434 }
435 #endif /* 0 */
436
437 return RULE_NOMATCH;
438 }
439
DNP3ObjEval(void * raw_packet,const uint8_t ** cursor,void * data)440 int DNP3ObjEval(void *raw_packet, const uint8_t **cursor, void *data)
441 {
442 SFSnortPacket *packet = (SFSnortPacket *)raw_packet;
443 MemBucket *tmp_bucket;
444 dnp3_option_data_t *rule_data = (dnp3_option_data_t *)data;
445 dnp3_session_data_t *session_data;
446 dnp3_reassembly_data_t *rdata;
447 uint8_t group, var;
448 uint8_t *obj_buffer;
449 uint16_t obj_buflen;
450 size_t header_size;
451 int rval = RULE_NOMATCH;
452
453 /* The preprocessor only evaluates PAF-flushed PDUs. If the rule options
454 don't check for this, they'll fire on stale session data when the
455 original packet goes through before flushing. */
456 if (packet->tcp_header && !PacketHasFullPDU(packet))
457 return RULE_NOMATCH;
458
459 /* For UDP packets, there is no PAF so we use the Alt Decode buffer. */
460 if (packet->udp_header && !_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE))
461 return RULE_NOMATCH;
462
463 tmp_bucket = (MemBucket *)
464 _dpd.sessionAPI->get_application_data(packet->stream_session, PP_DNP3);
465
466 if ((packet->payload_size == 0) || (tmp_bucket == NULL))
467 {
468 return RULE_NOMATCH;
469 }
470
471 session_data = (dnp3_session_data_t *)tmp_bucket->data;
472
473 if (session_data->direction == DNP3_CLIENT)
474 {
475 rdata = &(session_data->client_rdata);
476 header_size = sizeof(dnp3_app_request_header_t);
477 }
478 else
479 {
480 rdata = &(session_data->server_rdata);
481 header_size = sizeof(dnp3_app_response_header_t);
482 }
483
484 /* Only evaluate rules against complete Application-layer fragments */
485 if (rdata->state != DNP3_REASSEMBLY_STATE__DONE)
486 return RULE_NOMATCH;
487
488 /* Skip over the App request/response header.
489 They are different sizes, depending on whether it is a request or response! */
490 if (rdata->buflen < header_size)
491 return RULE_NOMATCH;
492
493 obj_buffer = (uint8_t *)rdata->buffer + header_size;
494 obj_buflen = rdata->buflen - header_size;
495
496 /* Rule parsing code combined our two arguments into a single uint16_t */
497 group = (rule_data->arg >> 8);
498 var = (rule_data->arg & 0x00FF);
499
500 rval = DNP3DecodeObject(obj_buffer, obj_buflen, group, var);
501
502 return rval;
503 }
504
DNP3IndEval(void * raw_packet,const uint8_t ** cursor,void * data)505 int DNP3IndEval(void *raw_packet, const uint8_t **cursor, void *data)
506 {
507 SFSnortPacket *packet = (SFSnortPacket *)raw_packet;
508 MemBucket *tmp_bucket;
509 dnp3_option_data_t *rule_data = (dnp3_option_data_t *)data;
510 dnp3_session_data_t *session_data;
511 dnp3_reassembly_data_t *rdata;
512
513 /* The preprocessor only evaluates PAF-flushed PDUs. If the rule options
514 don't check for this, they'll fire on stale session data when the
515 original packet goes through before flushing. */
516 if (packet->tcp_header && !PacketHasFullPDU(packet))
517 return RULE_NOMATCH;
518
519 /* For UDP packets, there is no PAF so we use the Alt Decode buffer. */
520 if (packet->udp_header && !_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE))
521 return RULE_NOMATCH;
522
523 tmp_bucket = (MemBucket *)
524 _dpd.sessionAPI->get_application_data(packet->stream_session, PP_DNP3);
525
526 if ((packet->payload_size == 0) || (tmp_bucket == NULL))
527 {
528 return RULE_NOMATCH;
529 }
530
531 session_data = (dnp3_session_data_t *)tmp_bucket->data;
532
533 /* Internal Indications only apply to DNP3 responses, not requests. */
534 if (session_data->direction == DNP3_CLIENT)
535 return RULE_NOMATCH;
536
537 rdata = &(session_data->server_rdata);
538
539 /* Only evaluate rules against complete Application-layer fragments */
540 if (rdata->state != DNP3_REASSEMBLY_STATE__DONE)
541 return RULE_NOMATCH;
542
543 if (session_data->indications & rule_data->arg)
544 return RULE_MATCH;
545
546 return RULE_NOMATCH;
547 }
548
DNP3DataEval(void * raw_packet,const uint8_t ** cursor,void * data)549 int DNP3DataEval(void *raw_packet, const uint8_t **cursor, void *data)
550 {
551 SFSnortPacket *packet = (SFSnortPacket *)raw_packet;
552 MemBucket *tmp_bucket;
553 dnp3_session_data_t *session_data;
554 dnp3_reassembly_data_t *rdata;
555
556 /* The preprocessor only evaluates PAF-flushed PDUs. If the rule options
557 don't check for this, they'll fire on stale session data when the
558 original packet goes through before flushing. */
559 if (packet->tcp_header && !PacketHasFullPDU(packet))
560 return RULE_NOMATCH;
561
562 /* For UDP packets, there is no PAF so we use the Alt Decode buffer. */
563 if (packet->udp_header && !_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE))
564 return RULE_NOMATCH;
565
566 tmp_bucket = (MemBucket *)
567 _dpd.sessionAPI->get_application_data(packet->stream_session, PP_DNP3);
568
569 if ((packet->payload_size == 0) || (tmp_bucket == NULL))
570 {
571 return RULE_NOMATCH;
572 }
573
574 session_data = (dnp3_session_data_t *)tmp_bucket->data;
575
576 if (session_data->direction == DNP3_CLIENT)
577 rdata = &(session_data->client_rdata);
578 else
579 rdata = &(session_data->server_rdata);
580
581 /* Only evaluate rules against complete Application-layer fragments */
582 if (rdata->state != DNP3_REASSEMBLY_STATE__DONE)
583 return RULE_NOMATCH;
584
585 /* Set the cursor to the reassembled Application-layer buffer */
586 *cursor = (uint8_t *)rdata->buffer;
587 _dpd.SetAltDetect((uint8_t *)rdata->buffer, rdata->buflen);
588
589 return RULE_MATCH;
590 }
591