1 /* $Id$ */
2 /*
3 ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4 ** Copyright (C) 2002-2013 Sourcefire, Inc.
5 ** Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
6 **
7 ** This program is free software; you can redistribute it and/or modify
8 ** it under the terms of the GNU General Public License Version 2 as
9 ** published by the Free Software Foundation. You may not use, modify or
10 ** distribute this program under any other version of the GNU General
11 ** Public License.
12 **
13 ** This program is distributed in the hope that it will be useful,
14 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ** GNU General Public License for more details.
17 **
18 ** You should have received a copy of the GNU General Public License
19 ** along with this program; if not, write to the Free Software
20 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23 /* Snort Detection Plugin Source File for IP Fragment Bits plugin */
24
25 /* sp_ip_fragbits
26 *
27 * Purpose:
28 *
29 * Check the fragmentation bits of the IP header for set values. Possible
30 * bits are don't fragment (DF), more fragments (MF), and reserved (RB).
31 *
32 * Arguments:
33 *
34 * The keyword to reference this plugin is "fragbits". Possible arguments are
35 * D, M and R for DF, MF and RB, respectively.
36 *
37 * Effect:
38 *
39 * Inidicates whether any of the specified bits have been set.
40 *
41 * Comments:
42 *
43 * Ofir Arkin should be a little happier now. :)
44 *
45 */
46
47 #ifdef HAVE_CONFIG_H
48 #include "config.h"
49 #endif
50
51 #include <sys/types.h>
52 #include <stdlib.h>
53 #include <ctype.h>
54 #include <string.h>
55
56 #include "sf_types.h"
57 #include "rules.h"
58 #include "treenodes.h"
59 #include "plugbase.h"
60 #include "decode.h"
61 #include "parser.h"
62 #include "snort_debug.h"
63 #include "util.h"
64 #include "plugin_enum.h"
65
66
67
68 #define GREATER_THAN 1
69 #define LESS_THAN 2
70
71
72 #define FB_NORMAL 0
73 #define FB_ALL 1
74 #define FB_ANY 2
75 #define FB_NOT 3
76
77 #define FB_RB 0x8000
78 #define FB_DF 0x4000
79 #define FB_MF 0x2000
80
81 #include "snort.h"
82 #include "profiler.h"
83 #ifdef PERF_PROFILING
84 PreprocStats fragBitsPerfStats;
85 PreprocStats fragOffsetPerfStats;
86 extern PreprocStats ruleOTNEvalPerfStats;
87 #endif
88
89 #include "sfhashfcn.h"
90 #include "detection_options.h"
91
92 typedef struct _FragBitsData
93 {
94 char mode;
95 uint16_t frag_bits;
96
97 } FragBitsData;
98
99
100 typedef struct _FragOffsetData
101 {
102 uint8_t comparison_flag;
103 uint8_t not_flag;
104 uint16_t offset;
105 } FragOffsetData;
106
107
108 void FragBitsInit(struct _SnortConfig *, char *, OptTreeNode *, int);
109 void ParseFragBits(struct _SnortConfig *, char *, OptTreeNode *);
110 int CheckFragBits(void *option_data, Packet *p);
111
112 /* offset checks */
113 void FragOffsetInit(struct _SnortConfig *, char *, OptTreeNode *, int);
114 void ParseFragOffset(struct _SnortConfig *, char *, OptTreeNode *);
115 int CheckFragOffset(void *option_data, Packet *p);
116
117 static uint16_t bitmask;
118
IpFragBitsCheckHash(void * d)119 uint32_t IpFragBitsCheckHash(void *d)
120 {
121 uint32_t a,b,c;
122 FragBitsData *data = (FragBitsData *)d;
123
124 a = data->mode;
125 b = data->frag_bits;
126 c = RULE_OPTION_TYPE_IP_FRAGBITS;
127
128 final(a,b,c);
129
130 return c;
131 }
132
IpFragBitsCheckCompare(void * l,void * r)133 int IpFragBitsCheckCompare(void *l, void *r)
134 {
135 FragBitsData *left = (FragBitsData *)l;
136 FragBitsData *right = (FragBitsData *)r;
137
138 if (!left || !right)
139 return DETECTION_OPTION_NOT_EQUAL;
140
141 if ((left->mode == right->mode) &&
142 (left->frag_bits == right->frag_bits))
143 {
144 return DETECTION_OPTION_EQUAL;
145 }
146
147 return DETECTION_OPTION_NOT_EQUAL;
148 }
149
IpFragOffsetCheckHash(void * d)150 uint32_t IpFragOffsetCheckHash(void *d)
151 {
152 uint32_t a,b,c;
153 FragOffsetData *data = (FragOffsetData *)d;
154
155 a = data->comparison_flag || (data->not_flag << 8);
156 b = data->offset;
157 c = RULE_OPTION_TYPE_IP_FRAG_OFFSET;
158
159 final(a,b,c);
160
161 return c;
162 }
163
IpFragOffsetCheckCompare(void * l,void * r)164 int IpFragOffsetCheckCompare(void *l, void *r)
165 {
166 FragOffsetData *left = (FragOffsetData *)l;
167 FragOffsetData *right = (FragOffsetData *)r;
168
169 if (!left || !right)
170 return DETECTION_OPTION_NOT_EQUAL;
171
172 if ((left->comparison_flag == right->comparison_flag) &&
173 (left->not_flag == right->not_flag) &&
174 (left->offset == right->offset))
175 {
176 return DETECTION_OPTION_EQUAL;
177 }
178
179 return DETECTION_OPTION_NOT_EQUAL;
180 }
181
182 /****************************************************************************
183 *
184 * Function: SetupFragBits()
185 *
186 * Purpose: Assign the keyword to the rules parser.
187 *
188 * Arguments: None.
189 *
190 * Returns: void function
191 *
192 ****************************************************************************/
SetupFragBits(void)193 void SetupFragBits(void)
194 {
195 /* map the keyword to an initialization/processing function */
196 RegisterRuleOption("fragbits", FragBitsInit, NULL, OPT_TYPE_DETECTION, NULL);
197 #ifdef PERF_PROFILING
198 RegisterPreprocessorProfile("fragbits", &fragBitsPerfStats, 3, &ruleOTNEvalPerfStats, NULL);
199 #endif
200
201 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Plugin: FragBits Setup\n"););
202 }
203
204
205 /****************************************************************************
206 *
207 * Function: FragBitsInit(struct _SnortConfig *, char *, OptTreeNode *)
208 *
209 * Purpose: Initialize the detection function and parse the arguments.
210 *
211 * Arguments: data => rule arguments/data
212 * otn => pointer to the current rule option list node
213 * protocol => protocol that must be specified to use this plugin
214 *
215 * Returns: void function
216 *
217 ****************************************************************************/
FragBitsInit(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)218 void FragBitsInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
219 {
220 OptFpList *fpl;
221 /* multiple declaration check */
222 if(otn->ds_list[PLUGIN_FRAG_BITS])
223 {
224 FatalError("%s(%d): Multiple fragbits options in rule\n", file_name,
225 file_line);
226 }
227
228 /* allocate the data structure and attach it to the
229 rule's data struct list */
230 otn->ds_list[PLUGIN_FRAG_BITS] = (FragBitsData *)
231 SnortAlloc(sizeof(FragBitsData));
232
233 /* this is where the keyword arguments are processed and placed into the
234 rule option's data structure */
235 ParseFragBits(sc, data, otn);
236
237 /*
238 * set the bitmask needed to mask off the IP offset field
239 * in the check function
240 */
241 bitmask = htons(0xE000);
242
243 /* finally, attach the option's detection function to the rule's
244 detect function pointer list */
245 fpl = AddOptFuncToList(CheckFragBits, otn);
246 fpl->type = RULE_OPTION_TYPE_IP_FRAGBITS;
247 fpl->context = otn->ds_list[PLUGIN_FRAG_BITS];
248 }
249
250
251
252 /****************************************************************************
253 *
254 * Function: ParseFragBits(struct _SnortConfig *, char *, OptTreeNode *)
255 *
256 * Purpose: This is the function that is used to process the option keyword's
257 * arguments and attach them to the rule's data structures.
258 *
259 * Arguments: data => argument data
260 * otn => pointer to the current rule's OTN
261 *
262 * Returns: void function
263 *
264 ****************************************************************************/
ParseFragBits(struct _SnortConfig * sc,char * data,OptTreeNode * otn)265 void ParseFragBits(struct _SnortConfig *sc, char *data, OptTreeNode *otn)
266 {
267 char *fptr;
268 char *fend;
269 FragBitsData *ds_ptr; /* data struct pointer */
270 void *ds_ptr_dup;
271
272 /* set the ds pointer to make it easier to reference the option's
273 particular data struct */
274 ds_ptr = otn->ds_list[PLUGIN_FRAG_BITS];
275
276 /* manipulate the option arguments here */
277 fptr = data;
278
279 while(isspace((u_char) *fptr))
280 {
281 fptr++;
282 }
283
284 if(strlen(fptr) == 0)
285 {
286 FatalError("[!] Line %s (%d): No arguments to the fragbits keyword\n", file_name, file_line);
287 }
288
289 fend = fptr + strlen(fptr);
290
291 ds_ptr->mode = FB_NORMAL; /* default value */
292
293 while(fptr < fend)
294 {
295 switch((*fptr&0xFF))
296 {
297 case 'd':
298 case 'D': /* don't frag bit */
299 ds_ptr->frag_bits |= FB_DF;
300 break;
301
302 case 'm':
303 case 'M': /* more frags bit */
304 ds_ptr->frag_bits |= FB_MF;
305 break;
306
307 case 'r':
308 case 'R': /* reserved bit */
309 ds_ptr->frag_bits |= FB_RB;
310 break;
311
312 case '!': /* NOT flag, fire if flags are not set */
313 ds_ptr->mode = FB_NOT;
314 break;
315
316 case '*': /* ANY flag, fire on any of these bits */
317 ds_ptr->mode = FB_ANY;
318 break;
319
320 case '+': /* ALL flag, fire on these bits plus any others */
321 ds_ptr->mode = FB_ALL;
322 break;
323
324 default:
325 FatalError("[!] Line %s (%d): Bad Frag Bits = \"%c\"\n"
326 " Valid options are: RDM+!*\n", file_name,
327 file_line, *fptr);
328 }
329
330 fptr++;
331 }
332
333 /* put the bits in network order for fast comparisons */
334 ds_ptr->frag_bits = htons(ds_ptr->frag_bits);
335
336 /* set the final option arguments here */
337 if (add_detection_option(sc, RULE_OPTION_TYPE_IP_FRAGBITS, (void *)ds_ptr, &ds_ptr_dup) == DETECTION_OPTION_EQUAL)
338 {
339 free(ds_ptr);
340 ds_ptr = otn->ds_list[PLUGIN_FRAG_BITS] = ds_ptr_dup;
341 }
342
343 }
344
345
346 /****************************************************************************
347 *
348 * Function: CheckFragBits(Packet *p, OptTreeNode *otn, OptFpList *fp_list)
349 *
350 * Purpose: This function checks the frag bits in the packets
351 *
352 * Arguments: data => argument data
353 * otn => pointer to the current rule's OTN
354 *
355 * Returns: If the mask matches return true, else return 0.
356 *
357 ****************************************************************************/
CheckFragBits(void * option_data,Packet * p)358 int CheckFragBits(void *option_data, Packet *p)
359 {
360 FragBitsData *fb = (FragBitsData *)option_data;
361 int rval = DETECTION_OPTION_NO_MATCH;
362 PROFILE_VARS;
363
364 if(!IPH_IS_VALID(p))
365 {
366 return rval;
367 }
368
369 PREPROC_PROFILE_START(fragBitsPerfStats);
370
371 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, " <!!> CheckFragBits: ");
372 DebugMessage(DEBUG_PLUGIN, "[rule: 0x%X:%d pkt: 0x%X] ",
373 fb->frag_bits, fb->mode, (GET_IPH_OFF(p)&bitmask)););
374
375 switch(fb->mode)
376 {
377 case FB_NORMAL:
378 /* check if the rule bits match the bits in the packet */
379 if(fb->frag_bits == (GET_IPH_OFF(p)&bitmask))
380 {
381 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Got Normal bits match\n"););
382 rval = DETECTION_OPTION_MATCH;
383 }
384 else
385 {
386 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Normal test failed\n"););
387 }
388 break;
389
390 case FB_NOT:
391 /* check if the rule bits don't match the bits in the packet */
392 if((fb->frag_bits & (GET_IPH_OFF(p)&bitmask)) == 0)
393 {
394 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Got NOT bits match\n"););
395 rval = DETECTION_OPTION_MATCH;
396 }
397 else
398 {
399 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"NOT test failed\n"););
400 }
401 break;
402
403 case FB_ALL:
404 /* check if the rule bits are present in the packet */
405 if((fb->frag_bits & (GET_IPH_OFF(p)&bitmask)) == fb->frag_bits)
406 {
407 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Got ALL bits match\n"););
408 rval = DETECTION_OPTION_MATCH;
409 }
410 else
411 {
412 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"ALL test failed\n"););
413 }
414 break;
415
416 case FB_ANY:
417 /* check if any of the rule bits match the bits in the packet */
418 if((fb->frag_bits & (GET_IPH_OFF(p)&bitmask)) != 0)
419 {
420 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Got ANY bits match\n"););
421 rval = DETECTION_OPTION_MATCH;
422 }
423 else
424 {
425 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"ANY test failed\n"););
426 }
427 break;
428 default:
429 break;
430 }
431
432 /* if the test isn't successful, this function *must* return 0 */
433 PREPROC_PROFILE_END(fragBitsPerfStats);
434 return rval;
435 }
436
437
438 /****************************************************************************
439 *
440 * Function: SetupFragOffset()
441 *
442 * Purpose: Assign the keyword to the rules parser.
443 *
444 * Arguments: None.
445 *
446 * Returns: void function
447 *
448 ****************************************************************************/
SetupFragOffset(void)449 void SetupFragOffset(void)
450 {
451 /* map the keyword to an initialization/processing function */
452 RegisterRuleOption("fragoffset", FragOffsetInit, NULL, OPT_TYPE_DETECTION, NULL);
453
454 #ifdef PERF_PROFILING
455 RegisterPreprocessorProfile("fragoffset", &fragOffsetPerfStats, 3, &ruleOTNEvalPerfStats, NULL);
456 #endif
457 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Plugin: FragOffset Setup\n"););
458 }
459
460 /****************************************************************************
461 *
462 * Function: FragOffsetInit(struct _SnortConfig *, char *, OptTreeNode *)
463 *
464 * Purpose: Initialize the detection function and parse the arguments.
465 *
466 * Arguments: data => rule arguments/data
467 * otn => pointer to the current rule option list node
468 * protocol => protocol that must be specified to use this plugin
469 *
470 * Returns: void function
471 *
472 ****************************************************************************/
FragOffsetInit(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)473 void FragOffsetInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
474 {
475 OptFpList *fpl;
476 /* allocate the data structure and attach it to the
477 rule's data struct list */
478 otn->ds_list[PLUGIN_FRAG_OFFSET] = (FragOffsetData *)SnortAlloc(sizeof(FragOffsetData));
479
480 /* this is where the keyword arguments are processed and placed into the
481 rule option's data structure */
482 ParseFragOffset(sc, data, otn);
483
484 /* finally, attach the option's detection function to the rule's
485 detect function pointer list */
486 fpl = AddOptFuncToList(CheckFragOffset, otn);
487 fpl->type = RULE_OPTION_TYPE_IP_FRAG_OFFSET;
488 fpl->context = otn->ds_list[PLUGIN_FRAG_OFFSET];
489 }
490
491
492 /****************************************************************************
493 *
494 * Function: ParseFragOffset(struct _SnortConfig *, char *, OptTreeNode *)
495 *
496 * Purpose: This is the function that is used to process the option keyword's
497 * arguments and attach them to the rule's data structures.
498 *
499 * Arguments: data => argument data
500 * otn => pointer to the current rule's OTN
501 *
502 * Returns: void function
503 *
504 ****************************************************************************/
ParseFragOffset(struct _SnortConfig * sc,char * data,OptTreeNode * otn)505 void ParseFragOffset(struct _SnortConfig *sc, char *data, OptTreeNode *otn)
506 {
507 char *fptr;
508 char *endTok;
509
510 FragOffsetData *ds_ptr; /* data struct pointer */
511 void *ds_ptr_dup;
512
513 /* set the ds pointer to make it easier to reference the option's
514 particular data struct */
515 ds_ptr = otn->ds_list[PLUGIN_FRAG_OFFSET];
516
517 /* manipulate the option arguments here */
518 fptr = data;
519
520 while(isspace((u_char) *fptr))
521 {
522 fptr++;
523 }
524
525 if(strlen(fptr) == 0)
526 {
527 FatalError("[!] Line %s (%d): No arguments to the fragoffset keyword\n", file_name, file_line);
528 }
529
530 if(*fptr == '!')
531 {
532 ds_ptr->not_flag = 1;
533 fptr++;
534 }
535
536 if(*fptr == '>')
537 {
538 if(!ds_ptr->not_flag)
539 {
540 ds_ptr->comparison_flag = GREATER_THAN;
541 fptr++;
542 }
543 }
544
545 if(*fptr == '<')
546 {
547 if(!ds_ptr->comparison_flag && !ds_ptr->not_flag)
548 {
549 ds_ptr->comparison_flag = LESS_THAN;
550 fptr++;
551 }
552 }
553
554 ds_ptr->offset = (uint16_t)SnortStrtoulRange(fptr, &endTok, 10, 0, UINT16_MAX);
555 if ((endTok == fptr) || (*endTok != '\0'))
556 {
557 FatalError("%s(%d) => Invalid parameter '%s' to fragoffset (not a "
558 "number?) \n", file_name, file_line, fptr);
559 }
560
561 if (add_detection_option(sc, RULE_OPTION_TYPE_IP_FRAG_OFFSET, (void *)ds_ptr, &ds_ptr_dup) == DETECTION_OPTION_EQUAL)
562 {
563 free(ds_ptr);
564 ds_ptr = otn->ds_list[PLUGIN_FRAG_OFFSET] = ds_ptr_dup;
565 }
566
567 }
568
569 /****************************************************************************
570 *
571 * Function: CheckFragOffset(char *, OptTreeNode *)
572 *
573 * Purpose: Use this function to perform the particular detection routine
574 * that this rule keyword is supposed to encompass.
575 *
576 * Arguments: data => argument data
577 * otn => pointer to the current rule's OTN
578 *
579 * Returns: If the detection test fails, this function *must* return a zero!
580 * On success, it calls the next function in the detection list
581 *
582 ****************************************************************************/
CheckFragOffset(void * option_data,Packet * p)583 int CheckFragOffset(void *option_data, Packet *p)
584 {
585 FragOffsetData *ipd = (FragOffsetData *)option_data;
586 int p_offset = p->frag_offset * 8;
587 int rval = DETECTION_OPTION_NO_MATCH;
588 PROFILE_VARS;
589
590 if(!IPH_IS_VALID(p))
591 {
592 return rval;
593 }
594
595 PREPROC_PROFILE_START(fragOffsetPerfStats);
596
597
598 #ifdef DEBUG_MSGS
599 DebugMessage(DEBUG_PLUGIN,
600 "[!] Checking fragoffset %d against %d\n",
601 ipd->offset, p->frag_offset * 8);
602
603 if(p->frag_flag)
604 {
605 DebugMessage(DEBUG_PLUGIN, "Frag Offset: 0x%04X Frag Size: 0x%04X\n",
606 (p->frag_offset & 0x1FFF) * 8,
607 (ntohs(GET_IPH_LEN(p)) - p->frag_offset - IP_HEADER_LEN));
608 }
609 #endif
610
611
612 if(!ipd->comparison_flag)
613 {
614 if((ipd->offset == p_offset) ^ ipd->not_flag)
615 {
616 rval = DETECTION_OPTION_MATCH;
617 }
618 else
619 {
620 /* you can put debug comments here or not */
621 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"No match\n"););
622 }
623 }
624 else
625 {
626 if(ipd->comparison_flag == GREATER_THAN)
627 {
628 if(p_offset > ipd->offset)
629 {
630 rval = DETECTION_OPTION_MATCH;
631 }
632 }
633 else
634 {
635 if(p_offset < ipd->offset)
636 {
637 rval = DETECTION_OPTION_MATCH;
638 }
639 }
640 }
641
642 /* if the test isn't successful, this function *must* return 0 */
643 PREPROC_PROFILE_END(fragOffsetPerfStats);
644 return rval;
645 }
646