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