1 /* $Id$ */
2 /*
3  ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4  ** Copyright (C) 1998-2013 Sourcefire, Inc.
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 /* sp_isdataat
23  *
24  * Purpose:
25  *    Test a specific byte to see if there is data.  (Basicly, rule keyword
26  *    into inBounds)
27  *
28  * Arguments:
29  *    <int>         byte location to check if there is data
30  *    ["relative"]  look for byte location relative to the end of the last
31  *                  pattern match
32  *    ["rawbytes"]  force use of the non-normalized buffer.
33  *
34  * Sample:
35  *   alert tcp any any -> any 110 (msg:"POP3 user overflow"; \
36  *      content:"USER"; isdataat:30,relative; content:!"|0a|"; within:30;)
37  */
38 
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42 
43 #include <sys/types.h>
44 #include <stdlib.h>
45 #include <ctype.h>
46 #ifdef HAVE_STRINGS_H
47 #include <strings.h>
48 #endif
49 #include <errno.h>
50 
51 #include "sf_types.h"
52 #include "snort_bounds.h"
53 #include "rules.h"
54 #include "treenodes.h"
55 #include "decode.h"
56 #include "plugbase.h"
57 #include "parser.h"
58 #include "snort_debug.h"
59 #include "util.h"
60 #include "mstring.h"
61 
62 #include "snort.h"
63 #include "profiler.h"
64 #include "sp_isdataat.h"
65 #include "sp_byte_extract.h"
66 #include "sp_byte_math.h"
67 #ifdef PERF_PROFILING
68 PreprocStats isDataAtPerfStats;
69 extern PreprocStats ruleOTNEvalPerfStats;
70 #endif
71 
72 #include "sfhashfcn.h"
73 #include "detection_options.h"
74 #include "detection_util.h"
75 
76 extern char *file_name;  /* this is the file name from rules.c, generally used
77                             for error messages */
78 
79 extern int file_line;    /* this is the file line number from rules.c that is
80                             used to indicate file lines for error messages */
81 
82 void IsDataAtInit(struct _SnortConfig *, char *, OptTreeNode *, int);
83 void IsDataAtParse(char *, IsDataAtData *, OptTreeNode *);
84 int  IsDataAt(void *option_data, Packet *p);
85 
IsDataAtHash(void * d)86 uint32_t IsDataAtHash(void *d)
87 {
88     uint32_t a,b,c;
89     IsDataAtData *data = (IsDataAtData *)d;
90 
91     a = data->offset;
92     b = data->flags;
93     c = RULE_OPTION_TYPE_IS_DATA_AT;
94 
95     mix(a,b,c);
96 
97     a += data->offset_var;
98 
99     final(a,b,c);
100 
101     return c;
102 }
103 
IsDataAtCompare(void * l,void * r)104 int IsDataAtCompare(void *l, void *r)
105 {
106     IsDataAtData *left = (IsDataAtData *)l;
107     IsDataAtData *right = (IsDataAtData *)r;
108 
109     if (!left || !right)
110         return DETECTION_OPTION_NOT_EQUAL;
111 
112     if (( left->offset == right->offset) &&
113         ( left->flags == right->flags) &&
114         ( left->offset_var == right->offset_var) )
115     {
116         return DETECTION_OPTION_EQUAL;
117     }
118 
119     return DETECTION_OPTION_NOT_EQUAL;
120 }
121 
122 /****************************************************************************
123  *
124  * Function: SetupIsDataAt()
125  *
126  * Purpose: Load 'er up
127  *
128  * Arguments: None.
129  *
130  * Returns: void function
131  *
132  ****************************************************************************/
SetupIsDataAt(void)133 void SetupIsDataAt(void)
134 {
135     /* map the keyword to an initialization/processing function */
136     RegisterRuleOption("isdataat", IsDataAtInit, NULL, OPT_TYPE_DETECTION, NULL);
137 #ifdef PERF_PROFILING
138     RegisterPreprocessorProfile("isdataat", &isDataAtPerfStats, 3, &ruleOTNEvalPerfStats, NULL);
139 #endif
140 
141     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: IsDataAt Setup\n"););
142 }
143 
144 
145 /****************************************************************************
146  *
147  * Function: IsDataAt(struct _SnortConfig *, char *, OptTreeNode *, int protocol)
148  *
149  * Purpose: Generic rule configuration function.  Handles parsing the rule
150  *          information and attaching the associated detection function to
151  *          the OTN.
152  *
153  * Arguments: data => rule arguments/data
154  *            otn => pointer to the current rule option list node
155  *            protocol => protocol the rule is on (we don't care in this case)
156  *
157  * Returns: void function
158  *
159  ****************************************************************************/
IsDataAtInit(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)160 void IsDataAtInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
161 {
162     IsDataAtData *idx;
163     OptFpList *fpl;
164     void *idx_dup;
165 
166     /* allocate the data structure and attach it to the
167        rule's data struct list */
168     idx = (IsDataAtData *) SnortAlloc(sizeof(IsDataAtData));
169 
170     if(idx == NULL)
171     {
172         FatalError("%s(%d): Unable to allocate IsDataAt data node\n",
173                 file_name, file_line);
174     }
175 
176     /* this is where the keyword arguments are processed and placed into the
177        rule option's data structure */
178     IsDataAtParse(data, idx, otn);
179 
180     if (add_detection_option(sc, RULE_OPTION_TYPE_IS_DATA_AT, (void *)idx, &idx_dup) == DETECTION_OPTION_EQUAL)
181     {
182         free(idx);
183         idx = idx_dup;
184      }
185 
186     fpl = AddOptFuncToList(IsDataAt, otn);
187     fpl->type = RULE_OPTION_TYPE_IS_DATA_AT;
188 
189     /* attach it to the context node so that we can call each instance
190      * individually
191      */
192     fpl->context = (void *) idx;
193 
194     if (idx->flags & ISDATAAT_RELATIVE_FLAG)
195         fpl->isRelative = 1;
196 }
197 
198 
199 
200 /****************************************************************************
201  *
202  * Function: IsDataAt(char *, IsDataAtData *, OptTreeNode *)
203  *
204  * Purpose: This is the function that is used to process the option keyword's
205  *          arguments and attach them to the rule's data structures.
206  *
207  * Arguments: data => argument data
208  *            idx => pointer to the processed argument storage
209  *            otn => pointer to the current rule's OTN
210  *
211  * Returns: void function
212  *
213  ****************************************************************************/
IsDataAtParse(char * data,IsDataAtData * idx,OptTreeNode * otn)214 void IsDataAtParse(char *data, IsDataAtData *idx, OptTreeNode *otn)
215 {
216     char **toks;
217     int num_toks;
218     int i;
219     char *cptr;
220     char *endp;
221     char *offset;
222     if(!data)
223     {
224 	    FatalError("%s (%d): Bad arguments to IsDataAt: (null)\n", file_name,file_line);
225     }
226 
227     toks = mSplit(data, ",", ISDATAAT_MAX_ARG, &num_toks, 0);
228     if(num_toks > ISDATAAT_MAX_ARG || num_toks < 1)
229     {
230 	    FatalError("%s (%d): Bad arguments to IsDataAt: %s\n", file_name,
231 			    file_line, data);
232     }
233     offset = toks[0];
234     if(*offset == '!')
235     {
236         idx->flags |= ISDATAAT_NOT_FLAG;
237         offset++;
238         while(isspace((int)*offset)) {offset++;}
239     }
240 
241     /* set how many bytes to process from the packet */
242     if (isdigit(offset[0]) || offset[0] == '-')
243     {
244         idx->offset = strtol(offset, &endp, 10);
245         idx->offset_var = -1;
246 
247         if(offset == endp)
248         {
249             FatalError("%s(%d): Unable to parse as byte value %s\n",
250                        file_name, file_line, toks[0]);
251         }
252 
253         if(idx->offset > 65535)
254         {
255             FatalError("%s(%d): IsDataAt offset greater than max IPV4 packet size",
256                     file_name, file_line);
257         }
258     }
259     else
260     {
261         idx->offset_var = find_value(offset);
262         if (idx->offset_var == BYTE_EXTRACT_NO_VAR)
263         {
264             ParseError(BYTE_EXTRACT_INVALID_ERR_FMT, "isdataat", toks[0]);
265         }
266     }
267 
268     for (i=1; i< num_toks; i++)
269     {
270         cptr = toks[i];
271 
272         while(isspace((int)*cptr)) {cptr++;}
273 
274         if(!strcasecmp(cptr, "relative"))
275         {
276             /* the offset is relative to the last pattern match */
277             idx->flags |= ISDATAAT_RELATIVE_FLAG;
278         }
279         else if(!strcasecmp(cptr, "rawbytes"))
280         {
281             /* the offset is to be applied to the non-normalized buffer */
282             idx->flags |= ISDATAAT_RAWBYTES_FLAG;
283         }
284         else
285         {
286             FatalError("%s(%d): unknown modifier \"%s\"\n",
287                     file_name, file_line, toks[1]);
288         }
289     }
290 
291     mSplitFree(&toks,num_toks);
292 }
293 
294 
295 /****************************************************************************
296  *
297  * Function: IsDataAt(char *, OptTreeNode *, OptFpList *)
298  *
299  * Purpose: Use this function to perform the particular detection routine
300  *          that this rule keyword is supposed to encompass.
301  *
302  * Arguments: p => pointer to the decoded packet
303  *            otn => pointer to the current rule's OTN
304  *            fp_list => pointer to the function pointer list
305  *
306  * Returns: If the detection test fails, this function *must* return a zero!
307  *          On success, it calls the next function in the detection list
308  *
309  ****************************************************************************/
IsDataAt(void * option_data,Packet * p)310 int IsDataAt(void *option_data, Packet *p)
311 {
312     IsDataAtData *isdata = (IsDataAtData *)option_data;
313     int rval = DETECTION_OPTION_NO_MATCH;
314     int dsize;
315     const uint8_t *base_ptr, *end_ptr, *start_ptr;
316     int search_start = 0;
317     PROFILE_VARS;
318 
319     PREPROC_PROFILE_START(isDataAtPerfStats);
320 
321     if (!isdata)
322     {
323         PREPROC_PROFILE_END(isDataAtPerfStats);
324         return rval;
325     }
326 
327     /* Get values from byte_math/byte_extract variables, if present. */
328     if (isdata->offset_var >= 0 )
329     {
330         if(isdata->offset_var == BYTE_MATH_VAR_INDEX )
331         {
332             isdata->offset = (int32_t) bytemath_variable;
333         }
334         else if(isdata->offset_var == COMMON_VAR_INDEX )
335         {
336             isdata->offset = (int32_t) common_var;
337         }
338         else if (isdata->offset_var < NUM_BYTE_EXTRACT_VARS)
339         {
340             GetByteExtractValue(&(isdata->offset), isdata->offset_var);
341         }
342     }
343 
344 
345     if (isdata->flags & ISDATAAT_RAWBYTES_FLAG)
346     {
347         /* Rawbytes specified, force use of that buffer */
348         dsize = p->dsize;
349         start_ptr = p->data;
350         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
351                     "Using RAWBYTES buffer!\n"););
352     }
353     else if (Is_DetectFlag(FLAG_ALT_DETECT))
354     {
355         dsize = DetectBuffer.len;
356         start_ptr = DetectBuffer.data;
357         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
358                 "Using Alternative Detect buffer!\n"););
359     }
360     else if(Is_DetectFlag(FLAG_ALT_DECODE))
361     {
362         /* If normalized buffer available, use it... */
363         dsize = DecodeBuffer.len;
364         start_ptr = DecodeBuffer.data;
365         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
366                     "Using Alternative Decode buffer!\n"););
367     }
368     else
369     {
370         if(IsLimitedDetect(p))
371             dsize = p->alt_dsize;
372         else
373             dsize = p->dsize;
374         start_ptr = p->data;
375     }
376 
377     base_ptr = start_ptr;
378     end_ptr = start_ptr + dsize;
379 
380     if((isdata->flags & ISDATAAT_RELATIVE_FLAG) && doe_ptr)
381     {
382         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
383                                 "Checking relative offset!\n"););
384 
385        /*  Because doe_ptr can be "end" in the last match,
386         *  use end + 1 for upper bound
387         *  Bound checked also after offset is applied
388         *
389         */
390         if(!inBounds(start_ptr, end_ptr + 1, doe_ptr))
391         {
392             DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
393                                     "[*] isdataat bounds check failed..\n"););
394             PREPROC_PROFILE_END(isDataAtPerfStats);
395             return rval;
396         }
397 
398         search_start = ( doe_ptr - start_ptr ) + isdata->offset;
399         base_ptr = doe_ptr;
400     }
401     else
402     {
403         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
404                                 "checking absolute offset %d\n", isdata->offset););
405         search_start = isdata->offset;
406         base_ptr = start_ptr;
407     }
408 
409     if ( search_start < 0 )
410     {
411         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
412                                 "[*] isdataat bounds check failed..\n"););
413         PREPROC_PROFILE_END(isDataAtPerfStats);
414         return rval;
415     }
416 
417     base_ptr = base_ptr + isdata->offset;
418 
419     if(inBounds(start_ptr, end_ptr, base_ptr))
420     {
421         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
422                     "[*] IsDataAt succeeded!  there is data...\n"););
423         rval = DETECTION_OPTION_MATCH;
424     }
425 
426     if (isdata->flags & ISDATAAT_NOT_FLAG)
427     {
428         rval = !rval;
429     }
430 
431 
432     /* otherwise dump */
433     PREPROC_PROFILE_END(isDataAtPerfStats);
434     return rval;
435 }
436