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 "util.h"
39 #include "snort_debug.h"
40 #include "plugin_enum.h"
41 #include "sfhashfcn.h"
42 
43 #include "snort.h"
44 #include "profiler.h"
45 #ifdef PERF_PROFILING
46 PreprocStats icmpCodePerfStats;
47 extern PreprocStats ruleOTNEvalPerfStats;
48 #endif
49 
50 #include "sfhashfcn.h"
51 #include "detection_options.h"
52 
53 typedef struct _IcmpCodeCheckData
54 {
55     /* the icmp code number */
56     int icmp_code;
57     int icmp_code2;
58     uint8_t operator;
59 } IcmpCodeCheckData;
60 
61 #define ICMP_CODE_TEST_EQ 1
62 #define ICMP_CODE_TEST_GT 2
63 #define ICMP_CODE_TEST_LT 3
64 #define ICMP_CODE_TEST_RG 4
65 
66 
67 void IcmpCodeCheckInit(struct _SnortConfig *, char *, OptTreeNode *, int);
68 void ParseIcmpCode(struct _SnortConfig *, char *, OptTreeNode *);
69 int IcmpCodeCheck(void *option_data, Packet *);
70 
IcmpCodeCheckHash(void * d)71 uint32_t IcmpCodeCheckHash(void *d)
72 {
73     uint32_t a,b,c;
74     IcmpCodeCheckData *data = (IcmpCodeCheckData *)d;
75 
76     a = data->icmp_code;
77     b = data->icmp_code2;
78     c = data->operator;
79 
80     mix(a,b,c);
81 
82     a += RULE_OPTION_TYPE_ICMP_CODE;
83 
84     final(a,b,c);
85 
86     return c;
87 }
88 
IcmpCodeCheckCompare(void * l,void * r)89 int IcmpCodeCheckCompare(void *l, void *r)
90 {
91     IcmpCodeCheckData *left = (IcmpCodeCheckData *)l;
92     IcmpCodeCheckData *right = (IcmpCodeCheckData *)r;
93 
94     if (!left || !right)
95         return DETECTION_OPTION_NOT_EQUAL;
96 
97     if ((left->icmp_code == right->icmp_code) &&
98         (left->icmp_code2 == right->icmp_code2) &&
99         (left->operator == right->operator))
100     {
101         return DETECTION_OPTION_EQUAL;
102     }
103 
104     return DETECTION_OPTION_NOT_EQUAL;
105 }
106 
107 /****************************************************************************
108  *
109  * Function: SetupIcmpCodeCheck()
110  *
111  * Purpose: Register the icode keyword and configuration function
112  *
113  * Arguments: None.
114  *
115  * Returns: void function
116  *
117  ****************************************************************************/
SetupIcmpCodeCheck(void)118 void SetupIcmpCodeCheck(void)
119 {
120     /* map the keyword to an initialization/processing function */
121     RegisterRuleOption("icode", IcmpCodeCheckInit, NULL, OPT_TYPE_DETECTION, NULL);
122 #ifdef PERF_PROFILING
123     RegisterPreprocessorProfile("icode", &icmpCodePerfStats, 3, &ruleOTNEvalPerfStats, NULL);
124 #endif
125 
126     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Plugin: IcmpCodeCheck Initialized\n"););
127 }
128 
129 
130 /****************************************************************************
131  *
132  * Function: IcmpCodeCheckInit(char *, OptTreeNode *)
133  *
134  * Purpose: Initialize the rule data structs and parse the rule argument
135  *          data, then link in the detection function
136  *
137  * Arguments: data => rule arguments/data
138  *            otn => pointer to the current rule option list node
139  *
140  * Returns: void function
141  *
142  ****************************************************************************/
IcmpCodeCheckInit(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)143 void IcmpCodeCheckInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
144 {
145     OptFpList *fpl;
146     if(protocol != IPPROTO_ICMP)
147     {
148         FatalError( "%s(%d): ICMP Options on non-ICMP rule\n", file_name, file_line);
149     }
150 
151     /* multiple declaration check */
152     if(otn->ds_list[PLUGIN_ICMP_CODE])
153     {
154         FatalError("%s(%d): Multiple icmp code options in rule\n", file_name,
155                 file_line);
156     }
157 
158     /* allocate the data structure and attach it to the
159        rule's data struct list */
160 
161     otn->ds_list[PLUGIN_ICMP_CODE] = (IcmpCodeCheckData *)
162             SnortAlloc(sizeof(IcmpCodeCheckData));
163 
164     /* this is where the keyword arguments are processed and placed into the
165        rule option's data structure */
166     ParseIcmpCode(sc, data, otn);
167 
168     /* finally, attach the option's detection function to the rule's
169        detect function pointer list */
170 
171     fpl = AddOptFuncToList(IcmpCodeCheck, otn);
172     fpl->type = RULE_OPTION_TYPE_ICMP_CODE;
173     fpl->context = otn->ds_list[PLUGIN_ICMP_CODE];
174 }
175 
176 
177 
178 /****************************************************************************
179  *
180  * Function: ParseIcmpCode(struct _SnortConfig *, char *, OptTreeNode *)
181  *
182  * Purpose: Process the icode argument and stick it in the data struct
183  *
184  * Arguments: data => argument data
185  *            otn => pointer to the current rule's OTN
186  *
187  * Returns: void function
188  *
189  ****************************************************************************/
ParseIcmpCode(struct _SnortConfig * sc,char * data,OptTreeNode * otn)190 void ParseIcmpCode(struct _SnortConfig *sc, char *data, OptTreeNode *otn)
191 {
192     char *code;
193     IcmpCodeCheckData *ds_ptr;  /* data struct pointer */
194     void *ds_ptr_dup;
195     char *endptr = NULL;
196 
197     /* set the ds pointer to make it easier to reference the option's
198        particular data struct */
199     ds_ptr = otn->ds_list[PLUGIN_ICMP_CODE];
200 
201     /* set a pointer to the data so to leave the original unchanged */
202     code = data;
203 
204     if(!data)
205     {
206         FatalError("%s (%d): No ICMP Code Specified\n",
207                    file_name, file_line);
208     }
209 
210 
211     /* get rid of whitespace before the data */
212     while(isspace((int)*data))
213         data++;
214 
215     if (*data == '\0')
216     {
217         FatalError("%s (%d): No ICMP Code Specified\n",
218                    file_name, file_line);
219     }
220 
221     /*
222      * If a range is specified, put the min in icmp_code, and the max in
223      * icmp_code2
224      */
225 
226     if ((isdigit((int)*data) || (*data == '-') || (*data == '+')) && strstr(data, "<>"))
227     {
228         ds_ptr->icmp_code = strtol(data, &endptr, 10);
229         while (isspace((int)*endptr))
230             endptr++;
231 
232         if (*endptr != '<')
233         {
234             FatalError("%s (%d): Invalid ICMP icode in rule: %s\n",
235                        file_name, file_line, code);
236         }
237 
238         data = endptr;
239 
240         data += 2;   /* move past <> */
241 
242         while (isspace((int)*data))
243             data++;
244 
245         ds_ptr->icmp_code2 = strtol(data, &endptr, 10);
246         if (*data == '\0' || *endptr != '\0')
247         {
248             FatalError("%s (%d): Invalid ICMP icode in rule: %s\n",
249                        file_name, file_line, code);
250         }
251 
252         if( (ds_ptr->icmp_code < -1) || (ds_ptr->icmp_code > 254) )
253              FatalError("%s (%d): Invalid ICMP icode lower limit in rule: %d\n",
254                        file_name, file_line, ds_ptr->icmp_code);
255 
256         if( (ds_ptr->icmp_code2 <= 0) || (ds_ptr->icmp_code2 > 256) )
257              FatalError("%s (%d): Invalid ICMP icode upper limit in rule: %d\n",
258                        file_name, file_line, ds_ptr->icmp_code2);
259 
260         if( (ds_ptr->icmp_code2 - ds_ptr->icmp_code) <= 1 )
261              FatalError("%s (%d): Invalid ICMP icode (upper-lower) <= 1 in rule: %s\n",
262                        file_name, file_line, code);
263 
264         ds_ptr->operator = ICMP_CODE_TEST_RG;
265     }
266     /* otherwise if its greater than... */
267     else if (*data == '>')
268     {
269         data++;
270         while (isspace((int)*data))
271             data++;
272 
273         ds_ptr->icmp_code = strtol(data, &endptr, 10);
274         if (*data == '\0' || *endptr != '\0')
275         {
276             FatalError("%s (%d): Invalid ICMP icode in rule: %s\n",
277                        file_name, file_line, code);
278         }
279 
280         if( (ds_ptr->icmp_code < 0) || (ds_ptr->icmp_code > 254) )
281              FatalError("%s (%d): Invalid ICMP icode lower limit in rule: %d\n",
282                        file_name, file_line, ds_ptr->icmp_code);
283 
284         ds_ptr->operator = ICMP_CODE_TEST_GT;
285     }
286     /* otherwise if its less than ... */
287     else if (*data == '<')
288     {
289         data++;
290         while (isspace((int)*data))
291             data++;
292 
293         ds_ptr->icmp_code = strtol(data, &endptr, 10);
294         if (*data == '\0' || *endptr != '\0')
295         {
296             FatalError("%s (%d): Invalid ICMP icode in rule: %s\n",
297                        file_name, file_line, code);
298         }
299 
300         if( (ds_ptr->icmp_code <= 0) || (ds_ptr->icmp_code > 256) )
301              FatalError("%s (%d): Invalid ICMP icode upper limit in rule: %d\n",
302                        file_name, file_line, ds_ptr->icmp_code);
303 
304         ds_ptr->operator = ICMP_CODE_TEST_LT;
305     }
306     /* otherwise check if its a digit */
307     else
308     {
309         ds_ptr->icmp_code = strtol(data, &endptr, 10);
310         if (*endptr != '\0')
311         {
312             FatalError("%s (%d): Invalid ICMP icode in rule: %s\n",
313                        file_name, file_line, code);
314         }
315 
316         if( (ds_ptr->icmp_code < 0) || (ds_ptr->icmp_code > 255) )
317              FatalError("%s (%d): Invalid ICMP icode upper limit in rule: %d\n",
318                        file_name, file_line, ds_ptr->icmp_code);
319 
320         ds_ptr->operator = ICMP_CODE_TEST_EQ;
321     }
322     if (add_detection_option(sc, RULE_OPTION_TYPE_ICMP_CODE, (void *)ds_ptr, &ds_ptr_dup) == DETECTION_OPTION_EQUAL)
323     {
324         otn->ds_list[PLUGIN_ICMP_CODE] = ds_ptr_dup;
325         free(ds_ptr);
326     }
327 
328     return;
329 }
330 
331 
332 /****************************************************************************
333  *
334  * Function: IcmpCodeCheck(Packet *p, OptTreeNode *, OptFpList *fp_list)
335  *
336  * Purpose: Test the packet's ICMP code field value against the option's
337  *          ICMP code
338  *
339  * Arguments: data => argument data
340  *            otn => pointer to the current rule's OTN
341  *
342  * Returns: void function
343  *
344  ****************************************************************************/
IcmpCodeCheck(void * option_data,Packet * p)345 int IcmpCodeCheck(void *option_data, Packet *p)
346 {
347     IcmpCodeCheckData *ds_ptr;
348     int rval = DETECTION_OPTION_NO_MATCH;
349     PROFILE_VARS;
350 
351     ds_ptr = (IcmpCodeCheckData *)option_data;
352 
353     /* return 0  if we don't have an icmp header */
354     if(!p->icmph)
355         return rval;
356 
357     PREPROC_PROFILE_START(icmpCodePerfStats);
358 
359     switch(ds_ptr->operator)
360     {
361         case ICMP_CODE_TEST_EQ:
362             if (ds_ptr->icmp_code == p->icmph->code)
363                 rval = DETECTION_OPTION_MATCH;
364             break;
365         case ICMP_CODE_TEST_GT:
366             if (p->icmph->code > ds_ptr->icmp_code)
367                 rval = DETECTION_OPTION_MATCH;
368             break;
369         case ICMP_CODE_TEST_LT:
370             if (p->icmph->code < ds_ptr->icmp_code)
371                 rval = DETECTION_OPTION_MATCH;
372             break;
373         case ICMP_CODE_TEST_RG:
374             if (p->icmph->code > ds_ptr->icmp_code &&
375                     p->icmph->code < ds_ptr->icmp_code2)
376                 rval = DETECTION_OPTION_MATCH;
377             break;
378         default:
379             break;
380     }
381 
382     DEBUG_WRAP(
383         if (rval == DETECTION_OPTION_MATCH)
384         {
385             DebugMessage(DEBUG_PLUGIN, "Got icmp code match!\n");
386         }
387         else
388         {
389             DebugMessage(DEBUG_PLUGIN, "Failed icmp code match!\n");
390         }
391         );
392 
393     PREPROC_PROFILE_END(icmpCodePerfStats);
394 
395     return rval;
396 }
397