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