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