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 /* sp_icmp_id
24 *
25 * Purpose:
26 *
27 * Test the ID field of ICMP ECHO and ECHO_REPLY packets for specified
28 * values. This is useful for detecting TFN attacks, amongst others.
29 *
30 * Arguments:
31 *
32 * The ICMP ID plugin takes a number as an option argument.
33 *
34 * Effect:
35 *
36 * Tests ICMP ECHO and ECHO_REPLY packet ID field values and returns a
37 * "positive" detection result (i.e. passthrough) upon a value match.
38 *
39 * Comments:
40 *
41 * This plugin was developed to detect TFN distributed attacks.
42 *
43 */
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #include <sys/types.h>
50 #include <stdlib.h>
51 #include <ctype.h>
52
53 #include "sf_types.h"
54 #include "rules.h"
55 #include "treenodes.h"
56 #include "decode.h"
57 #include "plugbase.h"
58 #include "parser.h"
59 #include "snort_debug.h"
60 #include "util.h"
61 #include "plugin_enum.h"
62
63 #include "snort.h"
64 #include "profiler.h"
65 #ifdef PERF_PROFILING
66 PreprocStats icmpIdPerfStats;
67 extern PreprocStats ruleOTNEvalPerfStats;
68 #endif
69
70 #include "sfhashfcn.h"
71 #include "detection_options.h"
72
73 typedef struct _IcmpIdCheckData
74 {
75 u_short icmpid;
76
77 } IcmpIdCheckData;
78
79 void ParseIcmpId(struct _SnortConfig *, char *, OptTreeNode *);
80 int IcmpIdCheck(void *option_data, Packet *);
81 void IcmpIdCheckInit(struct _SnortConfig *, char *, OptTreeNode *, int);
82
IcmpIdCheckHash(void * d)83 uint32_t IcmpIdCheckHash(void *d)
84 {
85 uint32_t a,b,c;
86 IcmpIdCheckData *data = (IcmpIdCheckData *)d;
87
88 a = data->icmpid;
89 b = RULE_OPTION_TYPE_ICMP_ID;
90 c = 0;
91
92 final(a,b,c);
93
94 return c;
95 }
96
IcmpIdCheckCompare(void * l,void * r)97 int IcmpIdCheckCompare(void *l, void *r)
98 {
99 IcmpIdCheckData *left = (IcmpIdCheckData *)l;
100 IcmpIdCheckData *right = (IcmpIdCheckData *)r;
101
102 if (!left || !right)
103 return DETECTION_OPTION_NOT_EQUAL;
104
105 if (left->icmpid == right->icmpid)
106 {
107 return DETECTION_OPTION_EQUAL;
108 }
109
110 return DETECTION_OPTION_NOT_EQUAL;
111 }
112
113 /****************************************************************************
114 *
115 * Function: SetupIcmpIdCheck()
116 *
117 * Purpose: Registers the configuration function and links it to a rule
118 * keyword. This is the function that gets called from InitPlugins
119 * in plugbase.c.
120 *
121 * Arguments: None.
122 *
123 * Returns: void function
124 *
125 ****************************************************************************/
SetupIcmpIdCheck(void)126 void SetupIcmpIdCheck(void)
127 {
128 /* map the keyword to an initialization/processing function */
129 RegisterRuleOption("icmp_id", IcmpIdCheckInit, NULL, OPT_TYPE_DETECTION, NULL);
130
131 #ifdef PERF_PROFILING
132 RegisterPreprocessorProfile("icmp_id", &icmpIdPerfStats, 3, &ruleOTNEvalPerfStats, NULL);
133 #endif
134 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: IcmpIdCheck Setup\n"););
135 }
136
137
138 /****************************************************************************
139 *
140 * Function: IcmpIdCheckInit(char *, OptTreeNode *)
141 *
142 * Purpose: Handles parsing the rule information and attaching the associated
143 * detection function to the OTN.
144 *
145 * Arguments: data => rule arguments/data
146 * otn => pointer to the current rule option list node
147 *
148 * Returns: void function
149 *
150 ****************************************************************************/
IcmpIdCheckInit(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)151 void IcmpIdCheckInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
152 {
153 OptFpList *fpl;
154 if(protocol != IPPROTO_ICMP)
155 {
156 FatalError("%s(%d): ICMP Options on non-ICMP rule\n", file_name, file_line);
157 }
158
159 /* multiple declaration check */
160 if(otn->ds_list[PLUGIN_ICMP_ID_CHECK])
161 {
162 FatalError("%s(%d): Multiple icmp id options in rule\n", file_name,
163 file_line);
164 }
165
166 /* allocate the data structure and attach it to the
167 rule's data struct list */
168 otn->ds_list[PLUGIN_ICMP_ID_CHECK] = (IcmpIdCheckData *)
169 SnortAlloc(sizeof(IcmpIdCheckData));
170
171 /* this is where the keyword arguments are processed and placed into the
172 rule option's data structure */
173
174 ParseIcmpId(sc, data, otn);
175
176 /* finally, attach the option's detection function to the rule's
177 detect function pointer list */
178 fpl = AddOptFuncToList(IcmpIdCheck, otn);
179 fpl->type = RULE_OPTION_TYPE_ICMP_ID;
180 fpl->context = otn->ds_list[PLUGIN_ICMP_ID_CHECK];
181 }
182
183
184
185 /****************************************************************************
186 *
187 * Function: ParseIcmpId(struct _SnortConfig *, char *, OptTreeNode *)
188 *
189 * Purpose: Convert the rule option argument to program data.
190 *
191 * Arguments: data => argument data
192 * otn => pointer to the current rule's OTN
193 *
194 * Returns: void function
195 *
196 ****************************************************************************/
ParseIcmpId(struct _SnortConfig * sc,char * data,OptTreeNode * otn)197 void ParseIcmpId(struct _SnortConfig *sc, char *data, OptTreeNode *otn)
198 {
199 IcmpIdCheckData *ds_ptr; /* data struct pointer */
200 void *ds_ptr_dup;
201 char *endTok;
202
203 /* set the ds pointer to make it easier to reference the option's
204 particular data struct */
205 ds_ptr = otn->ds_list[PLUGIN_ICMP_ID_CHECK];
206
207 /* advance past whitespace */
208 while(isspace((int)*data)) data++;
209
210 ds_ptr->icmpid = (uint16_t)SnortStrtoulRange(data, &endTok, 10, 0, UINT16_MAX);
211 if ((endTok == data) || (*endTok != '\0'))
212 {
213 FatalError("%s(%d) => Invalid parameter '%s' to icmp_id. "
214 "Must be between 0 & 65535, inclusive\n",
215 file_name, file_line, data);
216 }
217 ds_ptr->icmpid = htons(ds_ptr->icmpid);
218
219 if (add_detection_option(sc, RULE_OPTION_TYPE_ICMP_ID, (void *)ds_ptr, &ds_ptr_dup) == DETECTION_OPTION_EQUAL)
220 {
221 free(ds_ptr);
222 ds_ptr = otn->ds_list[PLUGIN_ICMP_ID_CHECK] = ds_ptr_dup;
223 }
224 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Set ICMP ID test value to %d\n", ds_ptr->icmpid););
225 }
226
227
228 /****************************************************************************
229 *
230 * Function: IcmpIdCheck(char *, OptTreeNode *)
231 *
232 * Purpose: Compare the ICMP ID field to the rule value.
233 *
234 * Arguments: data => argument data
235 * otn => pointer to the current rule's OTN
236 *
237 * Returns: If the detection test fails, this function *must* return a zero!
238 * On success, it calls the next function in the detection list
239 *
240 ****************************************************************************/
IcmpIdCheck(void * option_data,Packet * p)241 int IcmpIdCheck(void *option_data, Packet *p)
242 {
243 IcmpIdCheckData *icmpId = (IcmpIdCheckData *)option_data;
244 PROFILE_VARS;
245
246 if(!p->icmph)
247 return DETECTION_OPTION_NO_MATCH; /* if error occured while icmp header
248 * was processed, return 0 automagically. */
249
250 PREPROC_PROFILE_START(icmpIdPerfStats);
251
252 if( (p->icmph->type == ICMP_ECHO || p->icmph->type == ICMP_ECHOREPLY)
253 || (p->icmph->type == ICMP6_ECHO || p->icmph->type == ICMP6_REPLY)
254 )
255 {
256 /* test the rule ID value against the ICMP extension ID field */
257 if(icmpId->icmpid == p->icmph->s_icmp_id)
258 {
259 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "ICMP ID check success\n"););
260 PREPROC_PROFILE_END(icmpIdPerfStats);
261 return DETECTION_OPTION_MATCH;
262 }
263 else
264 {
265 /* you can put debug comments here or not */
266 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "ICMP ID check failed\n"););
267 }
268 }
269 PREPROC_PROFILE_END(icmpIdPerfStats);
270 return DETECTION_OPTION_NO_MATCH;
271 }
272