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