1 /*
2 ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 ** Copyright (C) 2002-2013 Sourcefire, Inc.
4 ** Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License Version 2 as
8 ** published by the Free Software Foundation.  You may not use, modify or
9 ** distribute this program under any other version of the GNU General
10 ** Public License.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software
19 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21 
22 /* $Id$ */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 
32 #include "sf_types.h"
33 #include "rules.h"
34 #include "treenodes.h"
35 #include "decode.h"
36 #include "plugbase.h"
37 #include "parser.h"
38 #include "snort_debug.h"
39 #include "util.h"
40 #include "plugin_enum.h"
41 #include "sp_icmp_type_check.h"
42 #include "sfhashfcn.h"
43 
44 #include "snort.h"
45 #include "profiler.h"
46 #ifdef PERF_PROFILING
47 PreprocStats icmpTypePerfStats;
48 extern PreprocStats ruleOTNEvalPerfStats;
49 #endif
50 
51 #include "sfhashfcn.h"
52 #include "detection_options.h"
53 
54 void IcmpTypeCheckInit(struct _SnortConfig *, char *, OptTreeNode *, int);
55 void ParseIcmpType(struct _SnortConfig *, char *, OptTreeNode *);
56 int IcmpTypeCheck(void *option_data, Packet *p);
57 
IcmpTypeCheckHash(void * d)58 uint32_t IcmpTypeCheckHash(void *d)
59 {
60     uint32_t a,b,c;
61     IcmpTypeCheckData *data = (IcmpTypeCheckData *)d;
62 
63     a = data->icmp_type;
64     b = data->icmp_type2;
65     c = data->operator;
66 
67     mix(a,b,c);
68 
69     a += RULE_OPTION_TYPE_ICMP_TYPE;
70 
71     final(a,b,c);
72 
73     return c;
74 }
75 
IcmpTypeCheckCompare(void * l,void * r)76 int IcmpTypeCheckCompare(void *l, void *r)
77 {
78     IcmpTypeCheckData *left = (IcmpTypeCheckData *)l;
79     IcmpTypeCheckData *right = (IcmpTypeCheckData *)r;
80 
81     if (!left || !right)
82         return DETECTION_OPTION_NOT_EQUAL;
83 
84     if ((left->icmp_type == right->icmp_type) &&
85         (left->icmp_type2 == right->icmp_type2) &&
86         (left->operator == right->operator))
87     {
88         return DETECTION_OPTION_EQUAL;
89     }
90 
91     return DETECTION_OPTION_NOT_EQUAL;
92 }
93 
94 /****************************************************************************
95  *
96  * Function: SetupIcmpTypeCheck()
97  *
98  * Purpose: Register the itype keyword and configuration function
99  *
100  * Arguments: None.
101  *
102  * Returns: void function
103  *
104  ****************************************************************************/
SetupIcmpTypeCheck(void)105 void SetupIcmpTypeCheck(void)
106 {
107     /* map the keyword to an initialization/processing function */
108     RegisterRuleOption("itype", IcmpTypeCheckInit, NULL, OPT_TYPE_DETECTION, NULL);
109 #ifdef PERF_PROFILING
110     RegisterPreprocessorProfile("itype", &icmpTypePerfStats, 3, &ruleOTNEvalPerfStats, NULL);
111 #endif
112     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: IcmpTypeCheck Initialized\n"););
113 }
114 
115 
116 /****************************************************************************
117  *
118  * Function: IcmpTypeCheckInit(struct _SnortConfig *, char *, OptTreeNode *)
119  *
120  * Purpose: Initialize the rule data structs and parse the rule argument
121  *          data, then link in the detection function
122  *
123  * Arguments: data => rule arguments/data
124  *            otn => pointer to the current rule option list node
125  *
126  * Returns: void function
127  *
128  ****************************************************************************/
IcmpTypeCheckInit(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)129 void IcmpTypeCheckInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
130 {
131     OptFpList *fpl;
132     if(protocol != IPPROTO_ICMP)
133     {
134         FatalError("%s(%d): ICMP Options on non-ICMP rule\n", file_name, file_line);
135     }
136 
137     /* multiple declaration check */
138     if(otn->ds_list[PLUGIN_ICMP_TYPE])
139     {
140         FatalError("%s(%d): Multiple ICMP type options in rule\n", file_name,
141                 file_line);
142     }
143 
144     /* allocate the data structure and attach it to the
145        rule's data struct list */
146     otn->ds_list[PLUGIN_ICMP_TYPE] = (IcmpTypeCheckData *)
147             SnortAlloc(sizeof(IcmpTypeCheckData));
148 
149     /* this is where the keyword arguments are processed and placed into the
150        rule option's data structure */
151     ParseIcmpType(sc, data, otn);
152 
153     /* finally, attach the option's detection function to the rule's
154        detect function pointer list */
155     fpl = AddOptFuncToList(IcmpTypeCheck, otn);
156     fpl->type = RULE_OPTION_TYPE_ICMP_TYPE;
157     fpl->context = otn->ds_list[PLUGIN_ICMP_TYPE];
158 }
159 
160 
161 
162 /****************************************************************************
163  *
164  * Function: ParseIcmpType(struct _SnortConfig *, char *, OptTreeNode *)
165  *
166  * Purpose: Process the itype argument and stick it in the data struct
167  *
168  * Arguments: data => argument data
169  *            otn => pointer to the current rule's OTN
170  *
171  * Returns: void function
172  *
173  ****************************************************************************/
ParseIcmpType(struct _SnortConfig * sc,char * data,OptTreeNode * otn)174 void ParseIcmpType(struct _SnortConfig *sc, char *data, OptTreeNode *otn)
175 {
176     char *type;
177     IcmpTypeCheckData *ds_ptr;  /* data struct pointer */
178     void *ds_ptr_dup;
179     char *endptr = NULL;
180 
181     /* set the ds pointer to make it easier to reference the option's
182        particular data struct */
183     ds_ptr = otn->ds_list[PLUGIN_ICMP_TYPE];
184 
185     /* set a pointer to the data so to leave the original unchanged */
186     type = data;
187 
188     if(!data)
189     {
190         FatalError("%s (%d): No ICMP Type Specified\n",
191                    file_name, file_line);
192     }
193 
194     /* get rid of spaces before the data */
195     while(isspace((int)*data))
196         data++;
197 
198     if (*data == '\0')
199     {
200         FatalError("%s (%d): No ICMP Type Specified : %s\n",
201                    file_name, file_line, type);
202     }
203 
204     /*
205      * if a range is specified, put the min in icmp_type, and the max in
206      * icmp_type2
207      */
208 
209     if (isdigit((int)*data) && strstr(data, "<>"))
210     {
211         ds_ptr->icmp_type = strtol(data, &endptr, 10);
212         while (isspace((int)*endptr))
213             endptr++;
214 
215         if (*endptr != '<')
216         {
217             FatalError("%s (%d): Invalid ICMP itype in rule: %s\n",
218                        file_name, file_line, type);
219         }
220 
221         data = endptr;
222 
223         data += 2;   /* move past <> */
224 
225         while (isspace((int)*data))
226             data++;
227 
228         ds_ptr->icmp_type2 = strtol(data, &endptr, 10);
229         if (*data == '\0' || *endptr != '\0')
230         {
231             FatalError("%s (%d): Invalid ICMP itype in rule: %s\n",
232                        file_name, file_line, type);
233         }
234 
235         ds_ptr->operator = ICMP_TYPE_TEST_RG;
236     }
237     /* otherwise if its greater than... */
238     else if (*data == '>')
239     {
240         data++;
241         while (isspace((int)*data))
242             data++;
243 
244         ds_ptr->icmp_type = strtol(data, &endptr, 10);
245         if (*data == '\0' || *endptr != '\0')
246         {
247             FatalError("%s (%d): Invalid ICMP itype in rule: %s\n",
248                        file_name, file_line, type);
249         }
250 
251         ds_ptr->operator = ICMP_TYPE_TEST_GT;
252     }
253     /* otherwise if its less than ... */
254     else if (*data == '<')
255     {
256         data++;
257         while (isspace((int)*data))
258             data++;
259 
260         ds_ptr->icmp_type = strtol(data, &endptr, 10);
261         if (*data == '\0' || *endptr != '\0')
262         {
263             FatalError("%s (%d): Invalid ICMP itype in rule: %s\n",
264                        file_name, file_line, type);
265         }
266 
267         ds_ptr->operator  = ICMP_TYPE_TEST_LT;
268     }
269     /* otherwise check if its a digit */
270     else
271     {
272         ds_ptr->icmp_type = strtol(data, &endptr, 10);
273         if (*endptr != '\0')
274         {
275             FatalError("%s (%d): Invalid ICMP itype in rule: %s\n",
276                        file_name, file_line, type);
277         }
278 
279         ds_ptr->operator = ICMP_TYPE_TEST_EQ;
280     }
281 
282     if (add_detection_option(sc, RULE_OPTION_TYPE_ICMP_TYPE, (void *)ds_ptr, &ds_ptr_dup) == DETECTION_OPTION_EQUAL)
283     {
284         free(ds_ptr);
285         ds_ptr = otn->ds_list[PLUGIN_ICMP_TYPE] = ds_ptr_dup;
286     }
287 
288     return;
289 }
290 
291 /****************************************************************************
292  *
293  * Function: IcmpTypeCheck(char *, OptTreeNode *)
294  *
295  * Purpose: Test the packet's ICMP type field value against the option's
296  *          ICMP type
297  *
298  * Arguments: data => argument data
299  *            otn => pointer to the current rule's OTN
300  *
301  * Returns: void function
302  *
303  ****************************************************************************/
IcmpTypeCheck(void * option_data,Packet * p)304 int IcmpTypeCheck(void *option_data, Packet *p)
305 {
306     IcmpTypeCheckData *ds_ptr;
307     int rval = DETECTION_OPTION_NO_MATCH;
308     PROFILE_VARS;
309 
310     ds_ptr = (IcmpTypeCheckData *)option_data;
311 
312     /* return 0  if we don't have an icmp header */
313     if(!p->icmph)
314         return rval;
315 
316     PREPROC_PROFILE_START(icmpTypePerfStats);
317 
318     switch(ds_ptr->operator)
319     {
320         case ICMP_TYPE_TEST_EQ:
321             if (p->icmph->type == ds_ptr->icmp_type)
322                 rval = DETECTION_OPTION_MATCH;
323             break;
324         case ICMP_TYPE_TEST_GT:
325             if (p->icmph->type > ds_ptr->icmp_type)
326                 rval = DETECTION_OPTION_MATCH;
327             break;
328         case ICMP_TYPE_TEST_LT:
329             if (p->icmph->type < ds_ptr->icmp_type)
330                 rval = DETECTION_OPTION_MATCH;
331             break;
332         case ICMP_TYPE_TEST_RG:
333             if (p->icmph->type > ds_ptr->icmp_type &&
334                     p->icmph->type < ds_ptr->icmp_type2)
335                 rval = DETECTION_OPTION_MATCH;
336             break;
337     }
338 
339     DEBUG_WRAP(
340         if (rval == DETECTION_OPTION_MATCH)
341         {
342             DebugMessage(DEBUG_PLUGIN, "Got icmp type match!\n");
343         }
344         else
345         {
346             DebugMessage(DEBUG_PLUGIN, "Failed icmp type match!\n");
347         }
348         );
349 
350     PREPROC_PROFILE_END(icmpTypePerfStats);
351     return rval;
352 }
353