1 /* $Id$ */
2 /*
3 ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4 ** Copyright (C) 2002-2013 Sourcefire, Inc.
5 ** Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
6 **
7 ** This program is free software; you can redistribute it and/or modify
8 ** it under the terms of the GNU General Public License Version 2 as
9 ** published by the Free Software Foundation. You may not use, modify or
10 ** distribute this program under any other version of the GNU General
11 ** Public License.
12 **
13 ** This program is distributed in the hope that it will be useful,
14 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ** GNU General Public License for more details.
17 **
18 ** You should have received a copy of the GNU General Public License
19 ** along with this program; if not, write to the Free Software
20 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23 /*
24 * 06/07/2007 - tw
25 * Commented out 'content-list' code since it's considered broken and there
26 * are no plans to fix it
27 */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <errno.h>
34 #ifdef HAVE_STRINGS_H
35 # include <strings.h>
36 #endif
37 #ifdef DEBUG_MSGS
38 # include <assert.h>
39 #endif
40
41 #include "sf_types.h"
42 #include "sp_pattern_match.h"
43 #include "sp_replace.h"
44 #include "snort_bounds.h"
45 #include "rules.h"
46 #include "treenodes.h"
47 #include "plugbase.h"
48 #include "snort_debug.h"
49 #include "mstring.h"
50 #include "hashstring.h"
51 #include "util.h"
52 #include "parser.h"
53 #include "plugin_enum.h"
54 #include "checksum.h"
55 #include "sfhashfcn.h"
56 #include "spp_httpinspect.h"
57 #include "snort.h"
58 #include "profiler.h"
59 #include "sfhashfcn.h"
60 #include "detection_options.h"
61 #include "sp_byte_extract.h"
62 #include "sp_byte_math.h"
63 #include "detection_util.h"
64 #include "sf_sechash.h"
65
66 /********************************************************************
67 * Macros
68 ********************************************************************/
69 #define MAX_PATTERN_SIZE 2048
70 #define PM_FP_ONLY "only"
71
72 /********************************************************************
73 * Global variables
74 ********************************************************************/
75 #ifdef PERF_PROFILING
76 PreprocStats contentPerfStats;
77 PreprocStats uricontentPerfStats;
78 #endif
79 int lastType = PLUGIN_PATTERN_MATCH;
80
81 #if 0
82 /* For OR patterns - not currently used */
83 int list_file_line; /* current line being processed in the list file */
84 #endif
85
86 /********************************************************************
87 * Extern variables
88 ********************************************************************/
89 #ifdef PERF_PROFILING
90 extern PreprocStats ruleOTNEvalPerfStats;
91 #endif
92
93 /********************************************************************
94 * Private function prototypes
95 ********************************************************************/
96 static void PayloadSearchInit(struct _SnortConfig *, char *, OptTreeNode *, int);
97 static void PayloadSearchUri(struct _SnortConfig *, char *, OptTreeNode *, int);
98 static void PayloadSearchHttpMethod(struct _SnortConfig *, char *, OptTreeNode *, int);
99 static void PayloadSearchHttpUri(struct _SnortConfig *, char *, OptTreeNode *, int);
100 static void PayloadSearchHttpHeader(struct _SnortConfig *, char *, OptTreeNode *, int);
101 static void PayloadSearchHttpCookie(struct _SnortConfig *, char *, OptTreeNode *, int);
102 static void PayloadSearchHttpBody(struct _SnortConfig *, char *, OptTreeNode *, int);
103 static void PayloadSearchHttpRawUri(struct _SnortConfig *, char *, OptTreeNode *, int);
104 static void PayloadSearchHttpRawHeader(struct _SnortConfig *, char *, OptTreeNode *, int);
105 //static void PayloadSearchHttpRawBody(struct _SnortConfig *, char *, OptTreeNode *, int);
106 static void PayloadSearchHttpRawCookie(struct _SnortConfig *, char *, OptTreeNode *, int);
107 static void PayloadSearchHttpStatCode(struct _SnortConfig *, char *, OptTreeNode *, int);
108 static void PayloadSearchHttpStatMsg(struct _SnortConfig *, char *, OptTreeNode *, int);
109 static void PayloadSearchOffset(struct _SnortConfig *, char *, OptTreeNode *, int);
110 static void PayloadSearchDepth(struct _SnortConfig *, char *, OptTreeNode *, int);
111 static void PayloadSearchDistance(struct _SnortConfig *, char *, OptTreeNode *, int);
112 static void PayloadSearchWithin(struct _SnortConfig *, char *, OptTreeNode *, int);
113 static void PayloadSearchNocase(struct _SnortConfig *, char *, OptTreeNode *, int);
114 static void PayloadSearchRawbytes(struct _SnortConfig *, char *, OptTreeNode *, int);
115 static void PayloadSearchFastPattern(struct _SnortConfig *, char *, OptTreeNode *, int);
116 static inline int HasFastPattern(OptTreeNode *, int);
117 static int32_t ParseInt(const char *, const char *);
118 static inline PatternMatchData * GetLastPmdError(OptTreeNode *, int, const char *);
119 static inline PatternMatchData * GetLastPmd(OptTreeNode *, int);
120 static void ValidateHttpContentModifiers(struct _SnortConfig *, PatternMatchData *);
121 static void MovePmdToUriDsList(OptTreeNode *, PatternMatchData *);
122 static char *PayloadExtractParameter(char *, int *);
123 static inline void ValidateContent(struct _SnortConfig *, PatternMatchData *, int);
124 static unsigned int GetMaxJumpSize(char *, int);
125 static int uniSearch(const char *, int, PatternMatchData *);
126 static int uniSearchReal(const char *data, int dlen, PatternMatchData *pmd, int nocase);
127 static int uniSearchHash(const char *data, int dlen, PatternMatchData *pmd);
128 static void PayloadSearchProtected(struct _SnortConfig *, char *, OptTreeNode *, int);
129 static void PayloadSearchHash(struct _SnortConfig *, char *, OptTreeNode *, int);
130 static void PayloadSearchLength(struct _SnortConfig *, char *, OptTreeNode *, int);
131
132 #if 0
133 /* Not currently used - DO NOT REMOVE */
134 static inline int computeDepth(int dlen, PatternMatchData * pmd);
135 static int uniSearchREG(char * data, int dlen, PatternMatchData * pmd);
136 #endif
137
138 #if 0
139 static const char *format_uri_buffer_str(int, int, char *);
140 static void PayloadSearchListInit(char *, OptTreeNode *, int);
141 static void ParseContentListFile(char *, OptTreeNode *, int);
142 static void PrintDupDOTPmds(PatternMatchData *pmd,
143 PatternMatchData *pmd_dup, option_type_t type)
144 #endif
145
146 /********************************************************************
147 * Setup and parsing functions
148 ********************************************************************/
SetupPatternMatch(void)149 void SetupPatternMatch(void)
150 {
151 /* initial pmd setup options */
152 RegisterRuleOption("content", PayloadSearchInit, NULL, OPT_TYPE_DETECTION, NULL);
153 RegisterRuleOption("uricontent", PayloadSearchUri, NULL, OPT_TYPE_DETECTION, NULL);
154 RegisterRuleOption("protected_content", PayloadSearchProtected, NULL, OPT_TYPE_DETECTION, NULL);
155
156 /* http content modifiers */
157 RegisterRuleOption("http_method", PayloadSearchHttpMethod, NULL, OPT_TYPE_DETECTION, NULL);
158 RegisterRuleOption("http_uri", PayloadSearchHttpUri, NULL, OPT_TYPE_DETECTION, NULL);
159 RegisterRuleOption("http_header", PayloadSearchHttpHeader, NULL, OPT_TYPE_DETECTION, NULL);
160 RegisterRuleOption("http_cookie", PayloadSearchHttpCookie, NULL, OPT_TYPE_DETECTION, NULL);
161 RegisterRuleOption("http_client_body", PayloadSearchHttpBody, NULL, OPT_TYPE_DETECTION, NULL);
162 RegisterRuleOption("http_raw_uri", PayloadSearchHttpRawUri, NULL, OPT_TYPE_DETECTION, NULL);
163 RegisterRuleOption("http_raw_header", PayloadSearchHttpRawHeader, NULL, OPT_TYPE_DETECTION, NULL);
164 /*RegisterRuleOption("http_raw_client_body", PayloadSearchHttpRawBody, NULL, OPT_TYPE_DETECTION, NULL);*/
165 RegisterRuleOption("http_raw_cookie", PayloadSearchHttpRawCookie, NULL, OPT_TYPE_DETECTION, NULL);
166 RegisterRuleOption("http_stat_code", PayloadSearchHttpStatCode, NULL, OPT_TYPE_DETECTION, NULL);
167 RegisterRuleOption("http_stat_msg", PayloadSearchHttpStatMsg, NULL, OPT_TYPE_DETECTION, NULL);
168
169 /* pattern offsets and depths */
170 RegisterRuleOption("offset", PayloadSearchOffset, NULL, OPT_TYPE_DETECTION, NULL);
171 RegisterRuleOption("depth", PayloadSearchDepth, NULL, OPT_TYPE_DETECTION, NULL);
172
173 /* distance and within are offset and depth, but relative to last match */
174 RegisterRuleOption("distance", PayloadSearchDistance, NULL, OPT_TYPE_DETECTION, NULL);
175 RegisterRuleOption("within", PayloadSearchWithin, NULL, OPT_TYPE_DETECTION, NULL);
176
177 /* other modifiers */
178 RegisterRuleOption("hash", PayloadSearchHash, NULL, OPT_TYPE_DETECTION, NULL);
179 RegisterRuleOption("length", PayloadSearchLength, NULL, OPT_TYPE_DETECTION, NULL);
180 RegisterRuleOption("nocase", PayloadSearchNocase, NULL, OPT_TYPE_DETECTION, NULL);
181 RegisterRuleOption("rawbytes", PayloadSearchRawbytes, NULL, OPT_TYPE_DETECTION, NULL);
182 RegisterRuleOption("fast_pattern", PayloadSearchFastPattern, NULL, OPT_TYPE_DETECTION, NULL);
183 RegisterRuleOption("replace", PayloadReplaceInit, NULL, OPT_TYPE_DETECTION, NULL);
184
185 #if 0
186 /* Not implemented yet */
187 RegisterRuleOption("content-list", PayloadSearchListInit, NULL, OPT_TYPE_DETECTION, NULL);
188 #endif
189
190 #ifdef PERF_PROFILING
191 RegisterPreprocessorProfile("content", &contentPerfStats, 3, &ruleOTNEvalPerfStats, NULL);
192 RegisterPreprocessorProfile("uricontent", &uricontentPerfStats, 3, &ruleOTNEvalPerfStats, NULL);
193 #endif
194 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
195 "Plugin: PatternMatch Initialized!\n"););
196 }
197
PayloadSearchInit(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)198 static void PayloadSearchInit(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
199 {
200 OptFpList *fpl;
201 PatternMatchData *pmd;
202 char *data_end;
203 char *data_dup;
204 char *opt_data;
205 int opt_len = 0;
206 char *next_opt;
207
208 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "In PayloadSearchInit()\n"););
209
210 /* whack a new node onto the list */
211 pmd = NewNode(otn, PLUGIN_PATTERN_MATCH);
212 lastType = PLUGIN_PATTERN_MATCH;
213
214 if (!data)
215 ParseError("No Content Pattern specified!");
216
217 data_dup = SnortStrdup(data);
218 data_end = data_dup + strlen(data_dup);
219
220 opt_data = PayloadExtractParameter(data_dup, &opt_len);
221
222 /* set up the pattern buffer */
223 ParsePattern(opt_data, otn, PLUGIN_PATTERN_MATCH);
224 next_opt = opt_data + opt_len;
225
226 /* link the plugin function in to the current OTN */
227 fpl = AddOptFuncToList(CheckANDPatternMatch, otn);
228 fpl->type = RULE_OPTION_TYPE_CONTENT;
229 pmd->buffer_func = CHECK_AND_PATTERN_MATCH;
230
231 fpl->context = pmd;
232 pmd->fpl = fpl;
233
234 // if content is followed by any comma separated options,
235 // we have to parse them here. content related options
236 // separated by semicolons go straight to the callbacks.
237 while (next_opt < data_end)
238 {
239 char **opts; /* dbl ptr for mSplit call, holds rule tokens */
240 int num_opts; /* holds number of tokens found by mSplit */
241 char* opt1;
242
243 next_opt++;
244 if (next_opt == data_end)
245 break;
246
247 opt_len = 0;
248 opt_data = PayloadExtractParameter(next_opt, &opt_len);
249 if (!opt_data)
250 break;
251
252 next_opt = opt_data + opt_len;
253
254 opts = mSplit(opt_data, " \t", 2, &num_opts, 0);
255
256 if (!opts)
257 continue;
258 opt1 = (num_opts == 2) ? opts[1] : NULL;
259
260 if (!strcasecmp(opts[0], "offset"))
261 {
262 PayloadSearchOffset(sc, opt1, otn, protocol);
263 }
264 else if (!strcasecmp(opts[0], "depth"))
265 {
266 PayloadSearchDepth(sc, opt1, otn, protocol);
267 }
268 else if (!strcasecmp(opts[0], "nocase"))
269 {
270 PayloadSearchNocase(sc, opt1, otn, protocol);
271 }
272 else if (!strcasecmp(opts[0], "rawbytes"))
273 {
274 PayloadSearchRawbytes(sc, opt1, otn, protocol);
275 }
276 else if (!strcasecmp(opts[0], "http_uri"))
277 {
278 PayloadSearchHttpUri(sc, opt1, otn, protocol);
279 }
280 else if (!strcasecmp(opts[0], "http_client_body"))
281 {
282 PayloadSearchHttpBody(sc, opt1, otn, protocol);
283 }
284 else if (!strcasecmp(opts[0], "http_header"))
285 {
286 PayloadSearchHttpHeader(sc, opt1, otn, protocol);
287 }
288 else if (!strcasecmp(opts[0], "http_method"))
289 {
290 PayloadSearchHttpMethod(sc, opt1, otn, protocol);
291 }
292 else if (!strcasecmp(opts[0], "http_cookie"))
293 {
294 PayloadSearchHttpCookie(sc, opt1, otn, protocol);
295 }
296 else if (!strcasecmp(opts[0], "http_raw_uri"))
297 {
298 PayloadSearchHttpRawUri(sc, opt1, otn, protocol);
299 }
300 else if (!strcasecmp(opts[0], "http_raw_header"))
301 {
302 PayloadSearchHttpRawHeader(sc, opt1, otn, protocol);
303 }
304 else if (!strcasecmp(opts[0], "http_raw_cookie"))
305 {
306 PayloadSearchHttpRawCookie(sc, opt1, otn, protocol);
307 }
308 else if (!strcasecmp(opts[0], "http_stat_code"))
309 {
310 PayloadSearchHttpStatCode(sc, opt1, otn, protocol);
311 }
312 else if (!strcasecmp(opts[0], "http_stat_msg"))
313 {
314 PayloadSearchHttpStatMsg(sc, opt1, otn, protocol);
315 }
316 else if (!strcasecmp(opts[0], "fast_pattern"))
317 {
318 PayloadSearchFastPattern(sc, opt1, otn, protocol);
319 }
320 else if (!strcasecmp(opts[0], "distance"))
321 {
322 PayloadSearchDistance(sc, opt1, otn, protocol);
323 }
324 else if (!strcasecmp(opts[0], "within"))
325 {
326 PayloadSearchWithin(sc, opt1, otn, protocol);
327 }
328 else if (!strcasecmp(opts[0], "replace"))
329 {
330 PayloadReplaceInit(sc, opt1, otn, protocol);
331 }
332 else
333 {
334 ParseError("Invalid Content parameter specified!");
335 }
336 mSplitFree(&opts, num_opts);
337 }
338
339
340 free(data_dup);
341
342 if(pmd->use_doe == 1)
343 fpl->isRelative = 1;
344
345 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
346 "OTN function PatternMatch Added to rule!\n"););
347 }
348
PayloadSearchUri(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)349 static void PayloadSearchUri(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
350 {
351 PatternMatchData *pmd = NewNode(otn, PLUGIN_PATTERN_MATCH_URI);
352 OptFpList *fpl;
353
354 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "In PayloadSearchUri()\n"););
355
356 lastType = PLUGIN_PATTERN_MATCH_URI;
357
358 /* set up the pattern buffer */
359 ParsePattern(data, otn, PLUGIN_PATTERN_MATCH_URI);
360
361 pmd->http_buffer = HTTP_BUFFER_URI;
362
363 /* link the plugin function in to the current OTN */
364 fpl = AddOptFuncToList(CheckUriPatternMatch, otn);
365
366 fpl->type = RULE_OPTION_TYPE_CONTENT_URI;
367 pmd->buffer_func = CHECK_URI_PATTERN_MATCH;
368
369 fpl->context = pmd;
370 pmd->fpl = fpl;
371
372 if (pmd->use_doe == 1)
373 fpl->isRelative = 1;
374
375 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
376 "OTN function PatternMatch Added to rule!\n"););
377 }
378
PayloadSearchProtected(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)379 void PayloadSearchProtected(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
380 {
381 OptFpList *fpl;
382 PatternMatchData *pmd;
383
384 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "In PayloadSearchProtected()\n"););
385
386 /* whack a new node onto the list */
387 pmd = NewNode(otn, PLUGIN_PATTERN_MATCH);
388 lastType = PLUGIN_PATTERN_MATCH;
389
390 if (!data)
391 ParseError("No Protected Content Pattern specified!");
392
393 /* The default secure hash type is set in the SnortConfig */
394 pmd->pattern_type = sc->Default_Protected_Content_Hash_Type;
395
396 /* set up the pattern buffer */
397 ParseProtectedPattern(data, otn, PLUGIN_PATTERN_MATCH);
398
399 /* link the plugin function in to the current OTN */
400 fpl = AddOptFuncToList(CheckANDPatternMatch, otn);
401 fpl->type = RULE_OPTION_TYPE_CONTENT;
402 pmd->buffer_func = CHECK_AND_PATTERN_MATCH;
403 pmd->protected_pattern = true;
404
405 fpl->context = pmd;
406 pmd->fpl = fpl;
407
408 if(pmd->use_doe == 1)
409 fpl->isRelative = 1;
410
411 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
412 "OTN function PatternMatchProtected Added to rule!\n"););
413 }
PayloadSearchHttpMethod(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)414 static void PayloadSearchHttpMethod(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
415 {
416 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "http_method");
417
418 if (data != NULL)
419 ParseError("'http_method' does not take an argument");
420
421 if ( pmd->http_buffer )
422 ParseWarning("at most one http buffer can be specified per content option");
423
424 pmd->http_buffer = HTTP_BUFFER_METHOD;
425 MovePmdToUriDsList(otn, pmd);
426 }
427
PayloadSearchHttpUri(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)428 static void PayloadSearchHttpUri(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
429 {
430 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "http_uri");
431
432 if (data != NULL)
433 ParseError("'http_uri' does not take an argument");
434
435 if ( pmd->http_buffer )
436 ParseWarning("at most one http buffer can be specified per content option");
437
438 pmd->http_buffer = HTTP_BUFFER_URI;
439 MovePmdToUriDsList(otn, pmd);
440 }
441
PayloadSearchHttpHeader(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)442 static void PayloadSearchHttpHeader(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
443 {
444 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "http_header");
445
446 if (data != NULL)
447 ParseError("'http_header' does not take an argument");
448
449 if ( pmd->http_buffer )
450 ParseWarning("at most one http buffer can be specified per content option");
451
452 pmd->http_buffer = HTTP_BUFFER_HEADER;
453 MovePmdToUriDsList(otn, pmd);
454 }
455
PayloadSearchHttpCookie(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)456 static void PayloadSearchHttpCookie(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
457 {
458 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "http_cookie");
459
460 if (data != NULL)
461 ParseError("'http_cookie' does not take an argument");
462
463 if ( pmd->http_buffer )
464 ParseWarning("at most one http buffer can be specified per content option");
465
466 pmd->http_buffer = HTTP_BUFFER_COOKIE;
467 MovePmdToUriDsList(otn, pmd);
468 }
469
PayloadSearchHttpBody(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)470 static void PayloadSearchHttpBody(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
471 {
472 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "http_client_body");
473
474 if (data != NULL)
475 ParseError("'http_client_body' does not take an argument");
476
477 if ( pmd->http_buffer )
478 ParseWarning("at most one http buffer can be specified per content option");
479
480 pmd->http_buffer = HTTP_BUFFER_CLIENT_BODY;
481 MovePmdToUriDsList(otn, pmd);
482 }
483
PayloadSearchHttpRawUri(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)484 static void PayloadSearchHttpRawUri(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
485 {
486 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "http_raw_uri");
487
488 if (data != NULL)
489 ParseError("'http_raw_uri' does not take an argument");
490
491 if ( pmd->http_buffer )
492 ParseWarning("at most one http buffer can be specified per content option");
493
494 pmd->http_buffer = HTTP_BUFFER_RAW_URI;
495 MovePmdToUriDsList(otn, pmd);
496 }
497
PayloadSearchHttpRawHeader(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)498 static void PayloadSearchHttpRawHeader(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
499 {
500 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "http_raw_header");
501
502 if (data != NULL)
503 ParseError("'http_raw_header' does not take an argument");
504
505 if ( pmd->http_buffer )
506 ParseWarning("at most one http buffer can be specified per content option");
507
508 pmd->http_buffer = HTTP_BUFFER_RAW_HEADER;
509 MovePmdToUriDsList(otn, pmd);
510 }
PayloadSearchHttpRawCookie(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)511 static void PayloadSearchHttpRawCookie(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
512 {
513 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "http_raw_cookie");
514
515 if (data != NULL)
516 ParseError("'http_raw_cookie' does not take an argument");
517
518 if ( pmd->http_buffer )
519 ParseWarning("at most one http buffer can be specified per content option");
520
521 pmd->http_buffer = HTTP_BUFFER_RAW_COOKIE;
522 MovePmdToUriDsList(otn, pmd);
523 }
PayloadSearchHttpStatCode(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)524 static void PayloadSearchHttpStatCode(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
525 {
526 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "http_stat_code");
527
528 if (data != NULL)
529 ParseError("'http_stat_code' does not take an argument");
530
531 if ( pmd->http_buffer )
532 ParseWarning("at most one http buffer can be specified per content option");
533
534 pmd->http_buffer = HTTP_BUFFER_STAT_CODE;
535 MovePmdToUriDsList(otn, pmd);
536 }
PayloadSearchHttpStatMsg(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)537 static void PayloadSearchHttpStatMsg(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
538 {
539 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "http_stat_msg");
540
541 if (data != NULL)
542 ParseError("'http_stat_msg' does not take an argument");
543
544 if ( pmd->http_buffer )
545 ParseWarning("at most one http buffer can be specified per content option");
546
547 pmd->http_buffer = HTTP_BUFFER_STAT_MSG;
548 MovePmdToUriDsList(otn, pmd);
549 }
550
551 typedef enum {
552 CMF_DISTANCE = 0x1, CMF_WITHIN = 0x2, CMF_OFFSET = 0x4, CMF_DEPTH = 0x8, CMF_PROT = 0x10
553 } ContentModifierFlags;
554
GetCMF(PatternMatchData * pmd)555 static unsigned GetCMF (PatternMatchData* pmd)
556 {
557 unsigned cmf = 0;
558 if ( (pmd->distance != 0) || (pmd->distance_var != -1) ) cmf |= CMF_DISTANCE;
559 if ( (pmd->within != PMD_WITHIN_UNDEFINED) || (pmd->within_var != -1) ) cmf |= CMF_WITHIN;
560 if ( (pmd->offset != 0) || (pmd->offset_var != -1) ) cmf |= CMF_OFFSET;
561 if ( (pmd->depth != 0) || (pmd->depth_var != -1) ) cmf |= CMF_DEPTH;
562 if ( pmd->protected_pattern ) cmf |= CMF_PROT;
563 return cmf;
564 }
565
566 #define BAD_DISTANCE (CMF_DISTANCE | CMF_OFFSET | CMF_DEPTH)
567 #define BAD_WITHIN (CMF_WITHIN | CMF_OFFSET | CMF_DEPTH | CMF_PROT)
568 #define BAD_OFFSET (CMF_OFFSET | CMF_DISTANCE | CMF_WITHIN)
569 #define BAD_DEPTH (CMF_DEPTH | CMF_DISTANCE | CMF_WITHIN | CMF_PROT)
570
PayloadSearchOffset(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)571 static void PayloadSearchOffset(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
572 {
573 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "offset");
574
575 if ( GetCMF(pmd) & BAD_OFFSET )
576 ParseError("offset can't be used with itself, distance, or within");
577
578 if (data == NULL)
579 ParseError("Missing argument to 'offset' option");
580
581 if (isdigit(data[0]) || data[0] == '-')
582 {
583 pmd->offset = ParseInt(data, "offset");
584 }
585 else
586 {
587 pmd->offset_var = find_value(data);
588 if (pmd->offset_var == BYTE_EXTRACT_NO_VAR)
589 {
590 ParseError(BYTE_EXTRACT_INVALID_ERR_FMT, "offset", data);
591 }
592 }
593
594 DEBUG_WRAP(DebugMessage(DEBUG_PARSER, "Pattern offset = %d\n",
595 pmd->offset););
596 }
597
PayloadSearchDepth(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)598 static void PayloadSearchDepth(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
599 {
600 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "depth");
601
602 if ( GetCMF(pmd) & BAD_DEPTH )
603 ParseError("depth can't be used with itself, protected, distance, or within");
604
605 if (data == NULL)
606 ParseError("Missing argument to 'depth' option");
607
608 if (isdigit(data[0]) || data[0] == '-')
609 {
610 pmd->depth = ParseInt(data, "depth");
611
612 /* check to make sure that this the depth allows this rule to fire */
613 if ((!pmd->protected_pattern) && (pmd->depth < (int)pmd->pattern_size))
614 {
615 ParseError("The depth (%d) is less than the size of the content(%u)!",
616 pmd->depth, pmd->pattern_size);
617 }
618 }
619 else
620 {
621 pmd->depth_var = find_value(data);
622 if (pmd->depth_var == BYTE_EXTRACT_NO_VAR)
623 {
624 ParseError(BYTE_EXTRACT_INVALID_ERR_FMT, "depth", data);
625 }
626 }
627
628 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Pattern depth = %d\n",
629 pmd->depth););
630 }
631
PayloadSearchDistance(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)632 static void PayloadSearchDistance(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
633 {
634 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "distance");
635
636 if ( GetCMF(pmd) & BAD_DISTANCE )
637 ParseError("distance can't be used with itself, offset, or depth");
638
639 if (data == NULL)
640 ParseError("Missing argument to 'distance' option");
641
642 if (isdigit(data[0]) || data[0] == '-')
643 {
644 pmd->distance = ParseInt(data, "distance");
645 }
646 else
647 {
648 pmd->distance_var = find_value(data);
649 if (pmd->distance_var == BYTE_EXTRACT_NO_VAR)
650 {
651 ParseError(BYTE_EXTRACT_INVALID_ERR_FMT, "distance", data);
652 }
653 }
654
655 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Pattern distance = %d\n",
656 pmd->distance););
657
658 /* Only do a relative search if this is a normal content match. */
659 if (lastType == PLUGIN_PATTERN_MATCH || lastType == PLUGIN_PATTERN_MATCH_URI)
660 {
661 pmd->use_doe = 1;
662 pmd->fpl->isRelative = 1;
663 }
664 }
665
PayloadSearchWithin(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)666 static void PayloadSearchWithin(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
667 {
668 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "within");
669
670 if ( GetCMF(pmd) & BAD_WITHIN )
671 ParseError("within can't be used with itself, protected, offset, or depth");
672
673 if (data == NULL)
674 ParseError("Missing argument to 'within' option");
675
676 if (isdigit(data[0]) || data[0] == '-')
677 {
678 pmd->within = ParseInt(data, "within");
679
680 if (!pmd->protected_pattern && (pmd->within < pmd->pattern_size))
681 ParseError("within (%d) is smaller than size of pattern", pmd->within);
682 }
683 else
684 {
685 pmd->within_var = find_value(data);
686 if (pmd->within_var == BYTE_EXTRACT_NO_VAR)
687 {
688 ParseError(BYTE_EXTRACT_INVALID_ERR_FMT, "within", data);
689 }
690 }
691
692 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Pattern within = %d\n",
693 pmd->within););
694
695 /* Only do a relative search if this is a normal content match. */
696 if (lastType == PLUGIN_PATTERN_MATCH || lastType == PLUGIN_PATTERN_MATCH_URI)
697 {
698 pmd->use_doe = 1;
699 pmd->fpl->isRelative = 1;
700 }
701 }
702
PayloadSearchNocase(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)703 static void PayloadSearchNocase(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
704 {
705 unsigned int i;
706 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "nocase");
707
708 if (data != NULL)
709 ParseError("'nocase' does not take an argument");
710 if (pmd->protected_pattern)
711 ParseError("'nocase' not useable with protected content");
712
713 for (i = 0; i < pmd->pattern_size; i++)
714 pmd->pattern_buf[i] = toupper((int)pmd->pattern_buf[i]);
715
716 pmd->nocase = 1;
717
718 pmd->search = uniSearchCI;
719 make_precomp(pmd);
720 }
721
PayloadSearchRawbytes(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)722 static void PayloadSearchRawbytes(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
723 {
724 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "rawbytes");
725
726 if (data != NULL)
727 ParseError("'rawbytes' does not take an argument");
728
729 /* mark this as inspecting a raw pattern match rather than a
730 * decoded application buffer */
731 pmd->rawbytes = 1;
732 }
733
PayloadSearchFastPattern(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)734 static void PayloadSearchFastPattern(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
735 {
736 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "fast_pattern");
737
738 /* There can only be one fast pattern content in the rule, whether
739 * normal, http or other */
740 if (pmd->fp)
741 {
742 ParseError("Cannot set fast_pattern modifier more than once "
743 "for the same \"content\".");
744 }
745 if( pmd->protected_pattern )
746 ParseError("Cannot set fast_pattern modifier with protected content");
747
748 if (HasFastPattern(otn, PLUGIN_PATTERN_MATCH))
749 ParseError("Can only use the fast_pattern modifier once in a rule.");
750 if (HasFastPattern(otn, PLUGIN_PATTERN_MATCH_URI))
751 ParseError("Can only use the fast_pattern modifier once in a rule.");
752 //if (HasFastPattern(otn, PLUGIN_PATTERN_MATCH_OR))
753 // ParseError("Can only use the fast_pattern modifier once in a rule.");
754
755 pmd->fp = 1;
756
757 if (data != NULL)
758 {
759 char *error_str = "Rule option \"fast_pattern\": Invalid parameter: "
760 "\"%s\". Valid parameters are: \"only\" | <offset>,<length>. "
761 "Offset and length must be integers less than 65536, offset cannot "
762 "be negative, length must be positive and (offset + length) must "
763 "evaluate to less than or equal to the actual pattern length. "
764 "Pattern length: %u";
765
766 if (isdigit((int)*data))
767 {
768 /* Specifying offset and length of pattern to use for
769 * fast pattern matcher */
770
771 long int offset, length;
772 char *endptr;
773 char **toks;
774 int num_toks;
775
776 toks = mSplit(data, ",", 0, &num_toks, 0);
777 if (num_toks != 2)
778 {
779 mSplitFree(&toks, num_toks);
780 ParseError(error_str, data, pmd->pattern_size);
781 }
782
783 offset = SnortStrtol(toks[0], &endptr, 0);
784 if ((errno == ERANGE) || (*endptr != '\0')
785 || (offset < 0) || (offset > UINT16_MAX))
786 {
787 mSplitFree(&toks, num_toks);
788 ParseError(error_str, data, pmd->pattern_size);
789 }
790
791 length = SnortStrtol(toks[1], &endptr, 0);
792 if ((errno == ERANGE) || (*endptr != '\0')
793 || (length <= 0) || (length > UINT16_MAX))
794 {
795 mSplitFree(&toks, num_toks);
796 ParseError(error_str, data, pmd->pattern_size);
797 }
798
799 mSplitFree(&toks, num_toks);
800
801 if ((int)pmd->pattern_size < (offset + length))
802 ParseError(error_str, data, pmd->pattern_size);
803
804 pmd->fp_offset = (uint16_t)offset;
805 pmd->fp_length = (uint16_t)length;
806 }
807 else
808 {
809 /* Specifies that this content should only be used for
810 * fast pattern matching */
811
812 if (strcasecmp(data, PM_FP_ONLY) != 0)
813 ParseError(error_str, data, pmd->pattern_size);
814
815 pmd->fp_only = 1;
816 }
817 }
818 }
819
PayloadSearchHash(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)820 static void PayloadSearchHash(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
821 {
822 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "hash");
823
824 if (data == NULL)
825 ParseError("Missing argument to 'hash' option");
826
827 if (!pmd->protected_pattern)
828 ParseError("hash modifier is only valid with protected_content");
829
830 /* strip any whitespace for good measure */
831 while( (*data != '\0') && isspace(*data) )
832 data += 1;
833
834 if( (pmd->pattern_type = SecHash_Name2Type((const char *)data)) == SECHASH_NONE )
835 ParseError("Bad hash type: '%s'", data);
836
837 DEBUG_WRAP(DebugMessage(DEBUG_PARSER, "Hash type = %d\n",
838 pmd->pattern_type););
839 }
840
PayloadSearchLength(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)841 static void PayloadSearchLength(struct _SnortConfig *sc, char *data, OptTreeNode * otn, int protocol)
842 {
843 PatternMatchData *pmd = GetLastPmdError(otn, lastType, "length");
844
845 if (data == NULL)
846 ParseError("Missing argument to 'length' option");
847
848 if (!pmd->protected_pattern)
849 ParseError("length modifier is only valid with protected_content");
850
851 if (isdigit(data[0]))
852 {
853 pmd->protected_length = ParseInt(data, "length");
854 if( (pmd->protected_length <= 0) || (pmd->protected_length > 65536))
855 ParseError("length must be greater than zero");
856 }
857 else
858 {
859 ParseError("Illegal length: %s", data);
860 }
861
862 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Plaintext length = %d\n",
863 pmd->protected_length););
864 }
865
HasFastPattern(OptTreeNode * otn,int list_type)866 static inline int HasFastPattern(OptTreeNode *otn, int list_type)
867 {
868 PatternMatchData *tmp;
869
870 if ((otn == NULL) || (otn->ds_list[list_type] == NULL))
871 return 0;
872
873 for (tmp = otn->ds_list[list_type]; tmp != NULL; tmp = tmp->next)
874 {
875 if (tmp->fp)
876 return 1;
877 }
878
879 return 0;
880 }
881
NewNode(OptTreeNode * otn,int type)882 PatternMatchData * NewNode(OptTreeNode *otn, int type)
883 {
884 PatternMatchData *pmd = NULL;
885
886 if (otn->ds_list[type] == NULL)
887 {
888 otn->ds_list[type] = (PatternMatchData *)SnortAlloc(sizeof(PatternMatchData));
889 pmd = otn->ds_list[type];
890 }
891 else
892 {
893 pmd = GetLastPmd(otn, type);
894 if (pmd != NULL)
895 {
896 pmd->next = (PatternMatchData *)SnortAlloc(sizeof(PatternMatchData));
897 pmd->next->prev = pmd;
898 pmd = pmd->next;
899 }
900 else
901 {
902 return NULL;
903 }
904 }
905
906 /* Set any non-zero default values here. */
907 pmd->offset_var = BYTE_EXTRACT_NO_VAR;
908 pmd->depth_var = BYTE_EXTRACT_NO_VAR;
909 pmd->distance_var = BYTE_EXTRACT_NO_VAR;
910 pmd->within_var = BYTE_EXTRACT_NO_VAR;
911 pmd->within = PMD_WITHIN_UNDEFINED;
912
913 pmd->protected_pattern = false;
914 pmd->protected_length = 0;
915 return pmd;
916 }
917
PatternMatchFree(void * d)918 void PatternMatchFree(void *d)
919 {
920 PatternMatchData *pmd = (PatternMatchData *)d;
921
922 if (pmd == NULL)
923 return;
924
925 (void)RemovePmdFromList(pmd);
926
927 if (pmd->pattern_buf)
928 free(pmd->pattern_buf);
929 if (pmd->replace_buf)
930 free(pmd->replace_buf);
931 if(pmd->skip_stride)
932 free(pmd->skip_stride);
933 if(pmd->shift_stride)
934 free(pmd->shift_stride);
935
936 free(pmd);
937 }
938
ParseInt(const char * data,const char * tag)939 static int32_t ParseInt(const char* data, const char* tag)
940 {
941 int32_t value = 0;
942 char *endptr = NULL;
943
944 value = SnortStrtol(data, &endptr, 10);
945
946 if (*endptr)
947 ParseError("Invalid '%s' format.", tag);
948
949 if (errno == ERANGE)
950 ParseError("Range problem on '%s' value.", tag);
951
952 if ((value > 65535) || (value < -65535))
953 ParseError("'%s' must in -65535:65535", tag);
954
955 return value;
956 }
957
958 /* Used for content modifiers that are used as rule options - need to get the
959 * last pmd which is the one they are modifying. If there isn't a last pmd
960 * error that a content must be specified before the modifier */
GetLastPmdError(OptTreeNode * otn,int type,const char * option)961 static inline PatternMatchData * GetLastPmdError(OptTreeNode *otn, int type, const char *option)
962 {
963 PatternMatchData *pmd = GetLastPmd(otn, type);
964
965 if (pmd == NULL)
966 {
967 ParseError("Please place \"content\" rules before \"%s\" modifier",
968 option == NULL ? "unknown" : option);
969 }
970
971 return pmd;
972 }
973
974 /* Gets the last pmd in the ds_list specified */
GetLastPmd(OptTreeNode * otn,int type)975 static inline PatternMatchData * GetLastPmd(OptTreeNode *otn, int type)
976 {
977 PatternMatchData *pmd;
978
979 if ((otn == NULL) || (otn->ds_list[type] == NULL))
980 return NULL;
981
982 for (pmd = otn->ds_list[type]; pmd->next != NULL; pmd = pmd->next);
983 return pmd;
984 }
985
ValidateProtectedContentModifiers(struct _SnortConfig * sc,PatternMatchData * pmd)986 static void ValidateProtectedContentModifiers(struct _SnortConfig *sc, PatternMatchData *pmd)
987 {
988 unsigned int length;
989
990 if (pmd == NULL)
991 ParseError("Please place \"content\" rules before protected content modifiers");
992
993 if( (length = SecHash_Type2Length(pmd->pattern_type)) == 0 )
994 ParseError("Bad pattern type");
995
996 if( pmd->pattern_size != length )
997 ParseError("Bad protected pattern hash digest length");
998
999 /* We NEED a specified pattern length (for this implementation) */
1000 if( (pmd->protected_length <= 0) || (pmd->protected_length > 65536))
1001 ParseError("No length or invalid length specified for protected_content rule");
1002
1003 /* At this point, we have a properly specified protected content rule with a pattern length.
1004 Since have the pattern size, we can place it in ->pattern_size for the runtime
1005 processing. If/when protected content searching expands beyond fixed ('specified')
1006 patterns, this approach needs to be revisted. */
1007 pmd->pattern_size = pmd->protected_length;
1008
1009 }
1010
1011 /* Options that can't be used with http content modifiers. Additionally
1012 * http_inspect preprocessor needs to be enabled */
ValidateHttpContentModifiers(struct _SnortConfig * sc,PatternMatchData * pmd)1013 static void ValidateHttpContentModifiers(struct _SnortConfig *sc, PatternMatchData *pmd)
1014 {
1015 if (pmd == NULL)
1016 ParseError("Please place \"content\" rules before http content modifiers");
1017
1018 /* TBD-EDM - verify this is handled correctly */
1019 #if 0
1020 if (!IsPreprocEnabled(sc, PP_HTTPINSPECT))
1021 {
1022 ParseError("Please enable the HTTP Inspect preprocessor "
1023 "before using the http content modifiers");
1024 }
1025 #endif
1026
1027 if (pmd->replace_buf != NULL)
1028 {
1029 ParseError("\"replace\" option is not supported in conjunction with "
1030 "http content modifiers");
1031 }
1032
1033 if (pmd->rawbytes == 1)
1034 {
1035 ParseError("Cannot use 'rawbytes' and http content as modifiers for "
1036 "the same \"content\"");
1037 }
1038 }
1039
1040 /* This is used if we get an http content modifier, since specifying "content"
1041 * defaults to the PLUGIN_PATTERN_MATCH list. We need to move the pmd to the
1042 * PLUGIN_PATTERN_MATCH_URI list */
MovePmdToUriDsList(OptTreeNode * otn,PatternMatchData * pmd)1043 static void MovePmdToUriDsList(OptTreeNode *otn, PatternMatchData *pmd)
1044 {
1045 int type = PLUGIN_PATTERN_MATCH_URI;
1046
1047 /* It's not currently in the correct list */
1048 if (lastType != type)
1049 {
1050 /* Just in case it's moved from the middle of the list */
1051 if (pmd->prev != NULL)
1052 pmd->prev->next = pmd->next;
1053 if (pmd->next != NULL)
1054 pmd->next->prev = pmd->prev;
1055
1056 /* Reset pointers */
1057 pmd->next = NULL;
1058 pmd->prev = NULL;
1059
1060 if (otn->ds_list[type] == NULL)
1061 {
1062 otn->ds_list[type] = pmd;
1063 }
1064 else
1065 {
1066 /* Make it the last in the URI list */
1067 PatternMatchData *tmp;
1068 for (tmp = otn->ds_list[type]; tmp->next != NULL; tmp = tmp->next);
1069 tmp->next = pmd;
1070 pmd->prev = tmp;
1071 }
1072
1073 /* Set the last type to the URI list */
1074 lastType = type;
1075
1076 /* Reset these to URI type */
1077 pmd->fpl->OptTestFunc = CheckUriPatternMatch;
1078 pmd->fpl->type = RULE_OPTION_TYPE_CONTENT_URI;
1079 pmd->buffer_func = CHECK_URI_PATTERN_MATCH;
1080 }
1081 }
1082
1083 #if 0
1084 /* Not currently used */
1085 static void PrintDupDOTPmds(PatternMatchData *pmd,
1086 PatternMatchData *pmd_dup, option_type_t type)
1087 {
1088 int i;
1089
1090 if ((pmd == NULL) || (pmd_dup == NULL))
1091 return;
1092
1093 LogMessage("Duplicate %sContent:\n"
1094 "%d %d %d %d %d %d %d %d %d %d\n"
1095 "%d %d %d %d %d %d %d %d %d %d\n",
1096 option_type == RULE_OPTION_TYPE_CONTENT ? "" : "Uri",
1097 pmd->exception_flag,
1098 pmd->offset,
1099 pmd->depth,
1100 pmd->distance,
1101 pmd->within,
1102 pmd->rawbytes,
1103 pmd->nocase,
1104 pmd->use_doe,
1105 pmd->http_buffer,
1106 pmd->pattern_max_jump_size,
1107 pmd_dup->exception_flag,
1108 pmd_dup->offset,
1109 pmd_dup->depth,
1110 pmd_dup->distance,
1111 pmd_dup->within,
1112 pmd_dup->rawbytes,
1113 pmd_dup->nocase,
1114 pmd_dup->use_doe,
1115 pmd_dup->http_buffer,
1116 pmd_dup->pattern_max_jump_size);
1117
1118 for (i = 0; i < pmd->pattern_size; i++)
1119 LogMessage("0x%x 0x%x", pmd->pattern_buf[i], pmd_dup->pattern_buf[i]);
1120 LogMessage("\n");
1121 for (i = 0; i < pmd->replace_size; i++)
1122 LogMessage("0x%x 0x%x", pmd->replace_buf[i], pmd_dup->replace_buf[i]);
1123 LogMessage("\n");
1124 LogMessage("\n");
1125 }
1126 #endif
1127
1128 /********************************************************************
1129 * Functions for detection option tree hashing and comparison
1130 * and other detection option tree uses
1131 ********************************************************************/
PatternMatchHash(void * d)1132 uint32_t PatternMatchHash(void *d)
1133 {
1134 uint32_t a,b,c,tmp;
1135 unsigned int i,j,k,l;
1136 PatternMatchData *pmd = (PatternMatchData *)d;
1137
1138 a = pmd->exception_flag;
1139 b = pmd->offset;
1140 c = pmd->depth;
1141
1142 mix(a,b,c);
1143
1144 a += pmd->distance;
1145 b += pmd->within;
1146 c += pmd->rawbytes;
1147
1148 mix(a,b,c);
1149
1150 a += pmd->nocase;
1151 b += pmd->use_doe;
1152 c += pmd->http_buffer;
1153
1154 mix(a,b,c);
1155
1156 a += pmd->pattern_size;
1157 b += pmd->replace_size;
1158 c += pmd->pattern_max_jump_size;
1159
1160 mix(a,b,c);
1161
1162 for (i=0,j=0;i<pmd->pattern_size;i+=4)
1163 {
1164 tmp = 0;
1165 k = pmd->pattern_size - i;
1166 if (k > 4)
1167 k=4;
1168
1169 for (l=0;l<k;l++)
1170 {
1171 tmp |= *(pmd->pattern_buf + i + l) << l*8;
1172 }
1173
1174 switch (j)
1175 {
1176 case 0:
1177 a += tmp;
1178 break;
1179 case 1:
1180 b += tmp;
1181 break;
1182 case 2:
1183 c += tmp;
1184 break;
1185 }
1186 j++;
1187
1188 if (j == 3)
1189 {
1190 mix(a,b,c);
1191 j = 0;
1192 }
1193 }
1194
1195 for (i=0;i<pmd->replace_size;i+=4)
1196 {
1197 tmp = 0;
1198 k = pmd->replace_size - i;
1199 if (k > 4)
1200 k=4;
1201
1202 for (l=0;l<k;l++)
1203 {
1204 tmp |= *(pmd->replace_buf + i + l) << l*8;
1205 }
1206
1207 switch (j)
1208 {
1209 case 0:
1210 a += tmp;
1211 break;
1212 case 1:
1213 b += tmp;
1214 break;
1215 case 2:
1216 c += tmp;
1217 break;
1218 }
1219 j++;
1220
1221 if (j == 3)
1222 {
1223 mix(a,b,c);
1224 j = 0;
1225 }
1226 }
1227
1228 if (j != 0)
1229 {
1230 mix(a,b,c);
1231 }
1232
1233 if (pmd->http_buffer)
1234 {
1235 a += RULE_OPTION_TYPE_CONTENT_URI;
1236 }
1237 else
1238 {
1239 a += RULE_OPTION_TYPE_CONTENT;
1240 }
1241
1242 b += pmd->fp;
1243 c += pmd->fp_only;
1244
1245 mix(a,b,c);
1246
1247 a += pmd->fp_offset;
1248 b += pmd->fp_length;
1249 c += pmd->offset_var;
1250
1251 mix(a,b,c);
1252
1253 a += pmd->depth_var;
1254 b += pmd->distance_var;
1255 c += pmd->within_var;
1256
1257 if( pmd->protected_pattern )
1258 {
1259 mix(a,b,c);
1260 a += 1;
1261 b += pmd->pattern_type;
1262 c += pmd->protected_length;
1263 }
1264 final(a,b,c);
1265
1266 return c;
1267 }
1268
PatternMatchCompare(void * l,void * r)1269 int PatternMatchCompare(void *l, void *r)
1270 {
1271 PatternMatchData *left = (PatternMatchData *)l;
1272 PatternMatchData *right = (PatternMatchData *)r;
1273 unsigned int i;
1274
1275 if (!left || !right)
1276 return DETECTION_OPTION_NOT_EQUAL;
1277
1278 if (left->buffer_func != right->buffer_func)
1279 return DETECTION_OPTION_NOT_EQUAL;
1280
1281 /* Sizes will be most different, check that first */
1282 if ((left->pattern_size != right->pattern_size) ||
1283 (left->replace_size != right->replace_size) ||
1284 (left->nocase != right->nocase))
1285 return DETECTION_OPTION_NOT_EQUAL;
1286
1287 /* Next compare the patterns for uniqueness */
1288 if (left->pattern_size)
1289 {
1290 if (left->nocase)
1291 {
1292 /* If nocase is set, do case insensitive compare on pattern */
1293 for (i=0;i<left->pattern_size;i++)
1294 {
1295 if (toupper(left->pattern_buf[i]) != toupper(right->pattern_buf[i]))
1296 {
1297 return DETECTION_OPTION_NOT_EQUAL;
1298 }
1299 }
1300 }
1301 else
1302 {
1303 /* If nocase is not set, do case sensitive compare on pattern */
1304 if (memcmp(left->pattern_buf, right->pattern_buf, left->pattern_size) != 0)
1305 {
1306 return DETECTION_OPTION_NOT_EQUAL;
1307 }
1308 }
1309 }
1310
1311 /* Check the replace pattern if exists */
1312 if (left->replace_size)
1313 {
1314 if (memcmp(left->replace_buf, right->replace_buf, left->replace_size) != 0)
1315 {
1316 return DETECTION_OPTION_NOT_EQUAL;
1317 }
1318 }
1319
1320 /* Now check the rest of the options */
1321 if ((left->exception_flag == right->exception_flag) &&
1322 (left->offset == right->offset) &&
1323 (left->depth == right->depth) &&
1324 (left->distance == right->distance) &&
1325 (left->within == right->within) &&
1326 (left->rawbytes == right->rawbytes) &&
1327 (left->use_doe == right->use_doe) &&
1328 (left->http_buffer == right->http_buffer) &&
1329 (left->search == right->search) &&
1330 (left->pattern_max_jump_size == right->pattern_max_jump_size) &&
1331 (left->fp == right->fp) &&
1332 (left->fp_only == right->fp_only) &&
1333 (left->fp_offset == right->fp_offset) &&
1334 (left->fp_length == right->fp_length) &&
1335 (left->offset_var == right->offset_var) &&
1336 (left->depth_var == right->depth_var) &&
1337 (left->distance_var == right->distance_var) &&
1338 (left->within_var == right->within_var) )
1339 {
1340 return DETECTION_OPTION_EQUAL;
1341 }
1342
1343 return DETECTION_OPTION_NOT_EQUAL;
1344 }
1345
1346 /* This function is called in parser.c after the rule has been
1347 * completely parsed */
FinalizeContentUniqueness(struct _SnortConfig * sc,OptTreeNode * otn)1348 void FinalizeContentUniqueness(struct _SnortConfig *sc, OptTreeNode *otn)
1349 {
1350 OptFpList *opt_fp = otn->opt_func;
1351
1352 while (opt_fp)
1353 {
1354 if ((opt_fp->type == RULE_OPTION_TYPE_CONTENT)
1355 || (opt_fp->type == RULE_OPTION_TYPE_CONTENT_URI))
1356 {
1357 PatternMatchData *pmd = (PatternMatchData *)opt_fp->context;
1358 option_type_t option_type = opt_fp->type;
1359 void *pmd_dup;
1360
1361 /* Since each content modifier can be parsed as a rule option,
1362 * do this check now that the entire rule has been parsed */
1363 if (option_type == RULE_OPTION_TYPE_CONTENT_URI)
1364 ValidateContent(sc, pmd, PLUGIN_PATTERN_MATCH_URI);
1365 else
1366 ValidateContent(sc, pmd, PLUGIN_PATTERN_MATCH);
1367
1368 if (add_detection_option(sc, option_type, (void *)pmd, &pmd_dup) == DETECTION_OPTION_EQUAL)
1369 {
1370 /* Don't do anything if they are the same pointer. This might happen when
1371 * converting an so rule to a text rule via ConvertDynamicRule() in sf_convert_dynamic.c
1372 * since we need to iterate through the RTN list in the OTN to verify that for http
1373 * contents, the http_inspect preprocessor is enabled in the policy that is using a
1374 * rule with http contents. */
1375 if (pmd != pmd_dup)
1376 {
1377 #if 0
1378 PrintDupDOTPmds(pmd, (PatternMatchData *)pmd_dup, option_type);
1379 #endif
1380
1381 /* Hack since some places check for non-nullness of ds_list.
1382 * Beware of iterating the pmd lists after this point since
1383 * they'll be messed up - only check for non-nullness */
1384 if (option_type == RULE_OPTION_TYPE_CONTENT)
1385 {
1386 if (pmd == otn->ds_list[PLUGIN_PATTERN_MATCH])
1387 otn->ds_list[PLUGIN_PATTERN_MATCH] = pmd_dup;
1388 }
1389 else
1390 {
1391 if (pmd == otn->ds_list[PLUGIN_PATTERN_MATCH_URI])
1392 otn->ds_list[PLUGIN_PATTERN_MATCH_URI] = pmd_dup;
1393 }
1394
1395 PatternMatchFree(pmd);
1396 opt_fp->context = pmd_dup;
1397 }
1398 }
1399 #if 0
1400 else
1401 {
1402 LogMessage("Unique %sContent\n",
1403 (opt_fp->OptTestFunc == CheckANDPatternMatch) ? "" : "Uri");
1404 }
1405 #endif
1406 }
1407
1408 opt_fp = opt_fp->next;
1409 }
1410 }
1411
ValidateFastPattern(OptTreeNode * otn)1412 void ValidateFastPattern(OptTreeNode *otn)
1413 {
1414 OptFpList *fpl = NULL;
1415 int fp_only = 0;
1416
1417 for(fpl = otn->opt_func; fpl != NULL; fpl = fpl->next)
1418 {
1419 /* a relative option is following a fast_pattern:only and
1420 * there was no resets.
1421 */
1422 if (fp_only == 1)
1423 {
1424 if (fpl->isRelative)
1425 ParseWarning("relative rule option used after "
1426 "fast_pattern:only");
1427 }
1428
1429 /* reset the check if one of these are present.
1430 */
1431 if ((fpl->type == RULE_OPTION_TYPE_FILE_DATA) ||
1432 (fpl->type == RULE_OPTION_TYPE_PKT_DATA) ||
1433 (fpl->type == RULE_OPTION_TYPE_BASE64_DATA) ||
1434 (fpl->type == RULE_OPTION_TYPE_PCRE) ||
1435 (fpl->type == RULE_OPTION_TYPE_BYTE_JUMP) ||
1436 (fpl->type == RULE_OPTION_TYPE_BYTE_EXTRACT))
1437 {
1438 fp_only = 0;
1439 }
1440
1441 /* set/unset the check on content options.
1442 */
1443 if ((fpl->type == RULE_OPTION_TYPE_CONTENT) ||
1444 (fpl->type == RULE_OPTION_TYPE_CONTENT_URI))
1445 {
1446 PatternMatchData *pmd = (PatternMatchData *)fpl->context;
1447
1448 if (pmd->fp_only)
1449 fp_only = 1;
1450 else
1451 fp_only = 0;
1452 }
1453 }
1454 }
1455
make_precomp(PatternMatchData * idx)1456 void make_precomp(PatternMatchData * idx)
1457 {
1458 if(idx->skip_stride)
1459 free(idx->skip_stride);
1460 if(idx->shift_stride)
1461 free(idx->shift_stride);
1462
1463 idx->skip_stride = make_skip(idx->pattern_buf, idx->pattern_size);
1464
1465 idx->shift_stride = make_shift(idx->pattern_buf, idx->pattern_size);
1466 }
1467
PayloadExtractParameter(char * data,int * result_len)1468 static char *PayloadExtractParameter(char *data, int *result_len)
1469 {
1470 char *quote_one = NULL, *quote_two = NULL;
1471 char *comma = NULL;
1472
1473 quote_one = strchr(data, '"');
1474 if (quote_one)
1475 {
1476 quote_two = strchr(quote_one+1, '"');
1477 while ( quote_two && quote_two[-1] == '\\' )
1478 quote_two = strchr(quote_two+1, '"');
1479 }
1480
1481 if (quote_one && quote_two)
1482 {
1483 comma = strchr(quote_two, ',');
1484 }
1485 else if (!quote_one)
1486 {
1487 comma = strchr(data, ',');
1488 }
1489
1490 if (comma)
1491 {
1492 *result_len = comma - data;
1493 *comma = '\0';
1494 }
1495 else
1496 {
1497 *result_len = strlen(data);
1498 }
1499
1500 return data;
1501 }
1502
1503 /* Since each content modifier can be parsed as a rule option, do this check
1504 * after parsing the entire rule in FinalizeContentUniqueness() */
ValidateContent(struct _SnortConfig * sc,PatternMatchData * pmd,int type)1505 static inline void ValidateContent(struct _SnortConfig *sc, PatternMatchData *pmd, int type)
1506 {
1507 if (pmd == NULL)
1508 return;
1509
1510 if (pmd->fp)
1511 {
1512 if( pmd->protected_pattern )
1513 {
1514 ParseError("Cannot use the fast pattern modifier with protected content");
1515 }
1516 if ((type == PLUGIN_PATTERN_MATCH_URI) && !IsHttpBufFpEligible(pmd->http_buffer))
1517
1518 {
1519 ParseError("Cannot use the fast_pattern content modifier for a lone "
1520 "http cookie/http raw uri /http raw header /http raw cookie /status code / status msg /http method buffer content.");
1521 }
1522
1523 if (pmd->use_doe || (pmd->offset != 0) || (pmd->depth != 0))
1524 {
1525 if (pmd->exception_flag)
1526 {
1527 ParseError("Cannot use the fast_pattern modifier for negated, "
1528 "relative or non-zero offset/depth content searches.");
1529 }
1530
1531 if (pmd->fp_only)
1532 {
1533 ParseError("Fast pattern only contents cannot be relative or "
1534 "have non-zero offset/depth content modifiers.");
1535 }
1536 }
1537
1538 if (pmd->fp_only)
1539 {
1540 if (pmd->replace_buf != NULL)
1541 {
1542 ParseError("Fast pattern only contents cannot use "
1543 "replace modifier.");
1544 }
1545
1546 if (pmd->exception_flag)
1547 ParseError("Fast pattern only contents cannot be negated.");
1548 }
1549 }
1550
1551 if (type == PLUGIN_PATTERN_MATCH_URI)
1552 ValidateHttpContentModifiers(sc, pmd);
1553 if (pmd->protected_pattern)
1554 ValidateProtectedContentModifiers(sc, pmd);
1555 }
1556
1557 /****************************************************************************
1558 *
1559 * Function: GetMaxJumpSize(char *, int)
1560 *
1561 * Purpose: Find the maximum number of characters we can jump ahead
1562 * from the current offset when checking for this pattern again.
1563 *
1564 * Arguments: data => the pattern string
1565 * data_len => length of pattern string
1566 *
1567 * Returns: int => number of bytes before pattern repeats within itself
1568 *
1569 ***************************************************************************/
GetMaxJumpSize(char * data,int data_len)1570 static unsigned int GetMaxJumpSize(char *data, int data_len)
1571 {
1572 int i, j;
1573
1574 j = 0;
1575 for ( i = 1; i < data_len; i++ )
1576 {
1577 if ( data[j] != data[i] )
1578 {
1579 j = 0;
1580 continue;
1581 }
1582 if ( i == (data_len - 1) )
1583 {
1584 return (data_len - j - 1);
1585 }
1586 j++;
1587 }
1588 return data_len;
1589 }
1590
1591 /****************************************************************************
1592 *
1593 * Function: ParsePattern(char *)
1594 *
1595 * Purpose: Process the application layer patterns and attach them to the
1596 * appropriate rule. My god this is ugly code.
1597 *
1598 * Arguments: rule => the pattern string
1599 *
1600 * Returns: void function
1601 *
1602 ***************************************************************************/
ParsePattern(char * rule,OptTreeNode * otn,int type)1603 void ParsePattern(char *rule, OptTreeNode * otn, int type)
1604 {
1605 char tmp_buf[MAX_PATTERN_SIZE];
1606
1607 /* got enough ptrs for you? */
1608 char *start_ptr;
1609 char *end_ptr;
1610 char *idx;
1611 char *dummy_idx;
1612 char *dummy_end;
1613 char *tmp;
1614 char hex_buf[3];
1615 u_int dummy_size = 0;
1616 int size;
1617 int hexmode = 0;
1618 int hexsize = 0;
1619 int pending = 0;
1620 int cnt = 0;
1621 int literal = 0;
1622 int exception_flag = 0;
1623 PatternMatchData *ds_idx;
1624
1625 /* clear out the temp buffer */
1626 memset(tmp_buf, 0, MAX_PATTERN_SIZE);
1627
1628 if (rule == NULL)
1629 ParseError("ParsePattern Got Null enclosed in quotation marks (\")!");
1630
1631 while(isspace((int)*rule))
1632 rule++;
1633
1634 if(*rule == '!')
1635 {
1636 exception_flag = 1;
1637 while(isspace((int)*++rule));
1638 }
1639
1640 /* find the start of the data */
1641 start_ptr = strchr(rule, '"');
1642
1643 if (start_ptr != rule)
1644 ParseError("Content data needs to be enclosed in quotation marks (\")!");
1645
1646 /* move the start up from the beggining quotes */
1647 start_ptr++;
1648
1649 /* find the end of the data */
1650 end_ptr = strrchr(start_ptr, '"');
1651
1652 if (end_ptr == NULL)
1653 ParseError("Content data needs to be enclosed in quotation marks (\")!");
1654
1655 /* Move the null termination up a bit more */
1656 *end_ptr = '\0';
1657
1658 /* Is there anything other than whitespace after the trailing
1659 * double quote? */
1660 tmp = end_ptr + 1;
1661 while (*tmp != '\0' && isspace ((int)*tmp))
1662 tmp++;
1663
1664 if (strlen (tmp) > 0)
1665 {
1666 ParseError("Bad data (possibly due to missing semicolon) after "
1667 "trailing double quote.");
1668 }
1669
1670 /* how big is it?? */
1671 size = end_ptr - start_ptr;
1672
1673 /* uh, this shouldn't happen */
1674 if (size <= 0)
1675 ParseError("Bad pattern length!");
1676
1677 /* set all the pointers to the appropriate places... */
1678 idx = start_ptr;
1679
1680 /* set the indexes into the temp buffer */
1681 dummy_idx = tmp_buf;
1682 dummy_end = (dummy_idx + size);
1683
1684 /* why is this buffer so small? */
1685 memset(hex_buf, '0', 2);
1686 hex_buf[2] = '\0';
1687
1688 /* BEGIN BAD JUJU..... */
1689 while(idx < end_ptr)
1690 {
1691 if (dummy_size >= MAX_PATTERN_SIZE-1)
1692 {
1693 /* Have more data to parse and pattern is about to go beyond end of buffer */
1694 ParseError("ParsePattern() dummy buffer overflow, make a smaller "
1695 "pattern please! (Max size = %d)", MAX_PATTERN_SIZE-1);
1696 }
1697
1698 DEBUG_WRAP(DebugMessage(DEBUG_PARSER, "processing char: %c\n", *idx););
1699 switch(*idx)
1700 {
1701 case '|':
1702 DEBUG_WRAP(DebugMessage(DEBUG_PARSER, "Got bar... "););
1703 if(!literal)
1704 {
1705 DEBUG_WRAP(DebugMessage(DEBUG_PARSER, "not in literal mode... "););
1706 if(!hexmode)
1707 {
1708 DEBUG_WRAP(DebugMessage(DEBUG_PARSER, "Entering hexmode\n"););
1709 hexmode = 1;
1710 }
1711 else
1712 {
1713 DEBUG_WRAP(DebugMessage(DEBUG_PARSER, "Exiting hexmode\n"););
1714
1715 /*
1716 ** Hexmode is not even.
1717 */
1718 if(!hexsize || hexsize % 2)
1719 {
1720 ParseError("Content hexmode argument has invalid "
1721 "number of hex digits. The argument '%s' "
1722 "must contain a full even byte string.", start_ptr);
1723 }
1724
1725 hexmode = 0;
1726 pending = 0;
1727 }
1728
1729 if(hexmode)
1730 hexsize = 0;
1731 }
1732 else
1733 {
1734 DEBUG_WRAP(DebugMessage(DEBUG_PARSER, "literal set, Clearing\n"););
1735 literal = 0;
1736 tmp_buf[dummy_size] = start_ptr[cnt];
1737 dummy_size++;
1738 }
1739
1740 break;
1741
1742 case '\\':
1743 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Got literal char... "););
1744
1745 if(!literal)
1746 {
1747 /* Make sure the next char makes this a valid
1748 * escape sequence.
1749 */
1750 if (idx [1] != '\0' && strchr ("\\\":;", idx [1]) == NULL)
1751 {
1752 ParseError("Bad escape sequence starting with \"%s\".", idx);
1753 }
1754
1755 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Setting literal\n"););
1756
1757 literal = 1;
1758 }
1759 else
1760 {
1761 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Clearing literal\n"););
1762 tmp_buf[dummy_size] = start_ptr[cnt];
1763 literal = 0;
1764 dummy_size++;
1765 }
1766
1767 break;
1768 case '"':
1769 if (!literal)
1770 ParseError("Non-escaped '\"' character!");
1771 /* otherwise process the character as default */
1772 default:
1773 if(hexmode)
1774 {
1775 if(isxdigit((int) *idx))
1776 {
1777 hexsize++;
1778
1779 if(!pending)
1780 {
1781 hex_buf[0] = *idx;
1782 pending++;
1783 }
1784 else
1785 {
1786 hex_buf[1] = *idx;
1787 pending--;
1788
1789 if(dummy_idx < dummy_end)
1790 {
1791 tmp_buf[dummy_size] = (u_char)
1792 strtol(hex_buf, (char **) NULL, 16)&0xFF;
1793
1794 dummy_size++;
1795 memset(hex_buf, '0', 2);
1796 hex_buf[2] = '\0';
1797 }
1798 else
1799 {
1800 ParseError("ParsePattern() dummy buffer "
1801 "overflow, make a smaller pattern "
1802 "please! (Max size = %d)", MAX_PATTERN_SIZE-1);
1803 }
1804 }
1805 }
1806 else
1807 {
1808 if(*idx != ' ')
1809 {
1810 ParseError("What is this \"%c\"(0x%X) doing in "
1811 "your binary buffer? Valid hex values "
1812 "only please! (0x0 - 0xF) Position: %d",
1813 (char) *idx, (char) *idx, cnt);
1814 }
1815 }
1816 }
1817 else
1818 {
1819 if(*idx >= 0x1F && *idx <= 0x7e)
1820 {
1821 if(dummy_idx < dummy_end)
1822 {
1823 tmp_buf[dummy_size] = start_ptr[cnt];
1824 dummy_size++;
1825 }
1826 else
1827 {
1828 ParseError("ParsePattern() dummy buffer "
1829 "overflow, make a smaller pattern "
1830 "please! (Max size = %d)", MAX_PATTERN_SIZE-1);
1831 }
1832
1833 if(literal)
1834 {
1835 literal = 0;
1836 }
1837 }
1838 else
1839 {
1840 if(literal)
1841 {
1842 tmp_buf[dummy_size] = start_ptr[cnt];
1843 dummy_size++;
1844 DEBUG_WRAP(DebugMessage(DEBUG_PARSER, "Clearing literal\n"););
1845 literal = 0;
1846 }
1847 else
1848 {
1849 ParseError("Character value out of range, try a "
1850 "binary buffer.");
1851 }
1852 }
1853 }
1854
1855 break;
1856 }
1857
1858 dummy_idx++;
1859 idx++;
1860 cnt++;
1861 }
1862 /* ...END BAD JUJU */
1863
1864 /* error prunning */
1865
1866 if (literal)
1867 ParseError("Backslash escape is not completed.");
1868
1869 if (hexmode)
1870 ParseError("Hexmode is not completed.");
1871
1872 ds_idx = (PatternMatchData *) otn->ds_list[type];
1873
1874 while(ds_idx->next != NULL)
1875 ds_idx = ds_idx->next;
1876
1877 ds_idx->pattern_buf = (char *)SnortAlloc(dummy_size+1);
1878 memcpy(ds_idx->pattern_buf, tmp_buf, dummy_size);
1879
1880 ds_idx->pattern_size = dummy_size;
1881 ds_idx->search = uniSearch;
1882
1883 make_precomp(ds_idx);
1884 ds_idx->exception_flag = exception_flag;
1885
1886 ds_idx->pattern_max_jump_size = GetMaxJumpSize(ds_idx->pattern_buf, ds_idx->pattern_size);
1887 }
1888
HexToNybble(char Chr,uint8_t * Val)1889 static bool HexToNybble( char Chr, uint8_t *Val )
1890 {
1891 if( !isxdigit( (int)Chr ) )
1892 {
1893 *Val = 0;
1894 return( false );
1895 }
1896
1897 if( isdigit( Chr ) )
1898 *Val = (uint8_t)(Chr - '0');
1899 else
1900 *Val = (uint8_t)(((char)toupper(Chr) - 'A') + 10);
1901
1902 return( true );
1903 }
1904
HexToByte(char * Str,uint8_t * Val)1905 static bool HexToByte( char *Str, uint8_t *Val )
1906 {
1907 uint8_t nybble;
1908
1909 *Val = 0;
1910
1911 if( HexToNybble( *Str++, &nybble ) )
1912 {
1913 *Val = ((nybble & 0xf) << 4);
1914 if( HexToNybble( *Str, &nybble ) )
1915 {
1916 *Val |= (nybble & 0xf);
1917 return( true );
1918 }
1919 }
1920
1921 return( false );
1922 }
1923
1924 /****************************************************************************
1925 *
1926 * Function: ParseProtectedPattern(char *)
1927 *
1928 * Purpose: Process the application layer patterns and attach them to the
1929 * appropriate rule.
1930 *
1931 * Arguments: rule => the pattern string
1932 *
1933 * Returns: void function
1934 *
1935 ***************************************************************************/
ParseProtectedPattern(char * rule,OptTreeNode * otn,int type)1936 void ParseProtectedPattern(char *rule, OptTreeNode * otn, int type)
1937 {
1938 uint8_t tmp_buf[MAX_PATTERN_SIZE];
1939
1940 char *tmp;
1941 unsigned int pat_idx;
1942 int exception_flag = 0;
1943 PatternMatchData *ds_idx;
1944
1945 /* clear out the temp buffer */
1946 memset(tmp_buf, 0, MAX_PATTERN_SIZE);
1947
1948 if (rule == NULL)
1949 ParseError("ParsePattern Got Null enclosed in quotation marks (\")!");
1950
1951 while(isspace((int)*rule))
1952 rule++;
1953
1954 if( *rule == '!' )
1955 {
1956 exception_flag = 1;
1957 rule++;
1958 }
1959 else
1960 exception_flag = 0;
1961
1962 /* find the start of the data */
1963 while(isspace((int)*rule))
1964 rule++;
1965
1966 if (*rule++ != '"')
1967 ParseError("Protected content data needs to be enclosed in quotation marks (\")!");
1968
1969 /* find the end of the data */
1970 tmp = strrchr(rule, '"');
1971
1972 if (tmp == NULL)
1973 ParseError("Protected content data needs to be enclosed in quotation marks (\")!");
1974
1975 /* Terminate the pattern string */
1976 *tmp = '\0';
1977
1978 /* Is there anything other than whitespace after the trailing
1979 * double quote? */
1980 while (*++tmp != '\0')
1981 if(!isspace ((int)*tmp))
1982 ParseError("Bad data (possibly due to missing semicolon) after "
1983 "trailing double quote.");
1984
1985 pat_idx = 0; /* index into the pattern buffer */
1986
1987 while((*rule != '\0') && (pat_idx < MAX_PATTERN_SIZE))
1988 {
1989 if( !HexToByte( rule, &(tmp_buf[pat_idx]) ) )
1990 ParseError("Bad protected pattern");
1991
1992 rule += 2;
1993 pat_idx += 1;
1994 }
1995
1996 if( (*rule == '\0') && (pat_idx == 0) )
1997 ParseError("Zero protected pattern size");
1998
1999 if( (*rule != '\0') && (pat_idx == MAX_PATTERN_SIZE) )
2000 ParseError("Protected pattern too long");
2001
2002 ds_idx = (PatternMatchData *) otn->ds_list[type];
2003
2004 while(ds_idx->next != NULL)
2005 ds_idx = ds_idx->next;
2006
2007 ds_idx->pattern_buf = (char *)SnortAlloc(pat_idx);
2008 memcpy(ds_idx->pattern_buf, tmp_buf, pat_idx);
2009
2010 ds_idx->pattern_size = pat_idx;
2011 ds_idx->search = uniSearchHash;
2012
2013 ds_idx->exception_flag = exception_flag;
2014
2015 }
2016
2017 /*
2018 * hash search function.
2019 *
2020 * data = ptr to buffer to search
2021 * dlen = distance to the back of the buffer being tested, validated
2022 * against offset + depth before function entry (not distance/within)
2023 * pmd = pointer to pattern match data struct
2024 *
2025 * return 1 for found
2026 * return 0 for not found
2027 * return -1 for error (search out of bounds)
2028 */
uniSearchHash(const char * data,int dlen,PatternMatchData * pmd)2029 static int uniSearchHash(const char *data, int dlen, PatternMatchData *pmd)
2030 {
2031 /*
2032 * in theory computeDepth doesn't need to be called because the
2033 * depth + offset adjustments have been made by the calling function
2034 */
2035 int depth = dlen;
2036 int success = 0;
2037 const char *start_ptr = data;
2038 const char *end_ptr = data + dlen;
2039 const char *base_ptr = start_ptr;
2040 uint32_t extract_offset, extract_distance;
2041 int search_start = 0;
2042
2043 if(pmd->use_doe != 1)
2044 {
2045 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "NOT Using Doe Ptr\n"););
2046 UpdateDoePtr(NULL, 0); /* get rid of all our pattern match state */
2047 }
2048
2049 /* Get byte_math/byte_extract variables */
2050 if (pmd->offset_var >= 0 )
2051 {
2052 if(pmd->offset_var == BYTE_MATH_VAR_INDEX)
2053 {
2054 pmd->offset = (int32_t) bytemath_variable;
2055 }
2056 else if(pmd->offset_var == COMMON_VAR_INDEX)
2057 {
2058 pmd->offset = (int32_t) common_var;
2059 }
2060 else if (pmd->offset_var < NUM_BYTE_EXTRACT_VARS)
2061 {
2062 GetByteExtractValue(&extract_offset, pmd->offset_var);
2063 pmd->offset = (int32_t) extract_offset;
2064 }
2065 }
2066 if (pmd->distance_var >= 0 )
2067 {
2068 if(pmd->distance_var == BYTE_MATH_VAR_INDEX)
2069 {
2070 pmd->distance = (int32_t) bytemath_variable;
2071 }
2072 else if(pmd->distance_var == COMMON_VAR_INDEX)
2073 {
2074 pmd->distance = (int32_t) common_var;
2075 }
2076 else if (pmd->distance_var < NUM_BYTE_EXTRACT_VARS)
2077 {
2078 GetByteExtractValue(&extract_distance, pmd->distance_var);
2079 pmd->distance = (int32_t) extract_distance;
2080 }
2081 }
2082
2083 // Set our initial starting point
2084 if (doe_ptr)
2085 {
2086 // Sanity check to make sure the doe_ptr is within the buffer we're
2087 // searching. It could be at the very end of the buffer due to a
2088 // previous match, but may have a negative distance here.
2089 if (((char *)doe_ptr < start_ptr) || ((char *)doe_ptr > end_ptr))
2090 {
2091 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Returning because "
2092 "doe_ptr isn't within the buffer we're searching: "
2093 "start_ptr: %p, end_ptr: %p, doe_ptr: %p\n",
2094 start_ptr, end_ptr, doe_ptr););
2095 return -1;
2096 }
2097
2098 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2099 "Setting base_ptr to doe_ptr (%p)\n", doe_ptr););
2100
2101 base_ptr = (const char *)doe_ptr;
2102 depth = dlen - ((char *)doe_ptr - data);
2103 }
2104 else
2105 {
2106 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2107 "Setting base_ptr to start_ptr (%p)\n", start_ptr););
2108
2109 base_ptr = start_ptr;
2110 depth = dlen;
2111 }
2112
2113 // Adjust base_ptr and depth based on distance
2114 // or offset parameters.
2115 if (pmd->distance != 0)
2116 {
2117 // This covers the pmd->distance > buffer case
2118 if (pmd->distance > depth)
2119 {
2120 depth = 0;
2121 }
2122 else
2123 {
2124 search_start = (base_ptr - start_ptr) + pmd->distance;
2125 base_ptr += pmd->distance;
2126 depth -= pmd->distance;
2127 }
2128
2129 // If the distance is negative and puts us before start_ptr
2130 // set base_ptr to start_ptr and adjust depth based on protected_length.
2131 if (search_start < 0)
2132 {
2133 return -1;
2134 }
2135 else if ((int)pmd->pattern_size < depth)
2136 {
2137 depth = (int)pmd->pattern_size;
2138 }
2139 search_start = 0;
2140 }
2141 else if (pmd->offset != 0)
2142 {
2143 if (pmd->offset > depth)
2144 {
2145 depth = 0;
2146 }
2147 else
2148 {
2149 search_start = pmd->offset;
2150 base_ptr += pmd->offset;
2151 depth -= pmd->offset;
2152 }
2153
2154 // If the distance is negative and puts us before start_ptr
2155 // set base_ptr to start_ptr and adjust depth based on pmd->protected_length.
2156 if (search_start < 0)
2157 {
2158 return -1;
2159 }
2160 else if ((int)pmd->pattern_size < depth)
2161 {
2162 depth = (int)pmd->pattern_size;
2163 }
2164 }
2165
2166 // If the pattern size is greater than the amount of data we have to
2167 // search, there's no way we can match, but return 0 here for the
2168 // case where the match is inverted and there is at least some data.
2169 if ((int)pmd->pattern_size > depth)
2170 {
2171 if (pmd->exception_flag && (depth > 0))
2172 return 0;
2173
2174 return -1;
2175 }
2176
2177 #ifdef DEBUG_MSGS
2178 {
2179 char *hexbuf;
2180
2181 assert(depth <= dlen);
2182
2183 DebugMessage(DEBUG_PATTERN_MATCH, "uniSearchHash:\n ");
2184
2185 hexbuf = hex((u_char *)pmd->pattern_buf, pmd->pattern_size);
2186 DebugMessage(DEBUG_PATTERN_MATCH, " p->data: %p\n doe_ptr: %p\n "
2187 "base_ptr: %p\n depth: %d\n searching for: %s\n",
2188 data, doe_ptr, base_ptr, depth, hexbuf);
2189 free(hexbuf);
2190 }
2191 #endif /* DEBUG_MSGS */
2192
2193 success = hashSearchFixed(base_ptr, pmd->pattern_size,
2194 pmd->pattern_type, pmd->pattern_buf);
2195
2196 #ifdef DEBUG_MSGS
2197 if(success)
2198 {
2199 DebugMessage(DEBUG_PATTERN_MATCH, "matched, doe_ptr: %p (%d)\n",
2200 doe_ptr, ((char *)doe_ptr - data));
2201 }
2202 #endif
2203
2204 return success;
2205 }
2206
2207 /********************************************************************
2208 * Runtime functions
2209 ********************************************************************/
2210 /*
2211 * case sensitive search
2212 *
2213 * data = ptr to buffer to search
2214 * dlen = distance to the back of the buffer being tested, validated
2215 * against offset + depth before function entry (not distance/within)
2216 * pmd = pointer to pattern match data struct
2217 */
2218
uniSearch(const char * data,int dlen,PatternMatchData * pmd)2219 static int uniSearch(const char *data, int dlen, PatternMatchData *pmd)
2220 {
2221 return uniSearchReal(data, dlen, pmd, 0);
2222 }
2223
2224 /*
2225 * case insensitive search
2226 *
2227 * data = ptr to buffer to search
2228 * dlen = distance to the back of the buffer being tested, validated
2229 * against offset + depth before function entry (not distance/within)
2230 * pmd = pointer to pattern match data struct
2231 *
2232 * NOTE - this is used in sf_convert_dynamic.c so cannot be static
2233 */
uniSearchCI(const char * data,int dlen,PatternMatchData * pmd)2234 int uniSearchCI(const char *data, int dlen, PatternMatchData *pmd)
2235 {
2236 return uniSearchReal(data, dlen, pmd, 1);
2237 }
2238
2239 /*
2240 * single search function.
2241 *
2242 * data = ptr to buffer to search
2243 * dlen = distance to the back of the buffer being tested, validated
2244 * against offset + depth before function entry (not distance/within)
2245 * pmd = pointer to pattern match data struct
2246 * nocase = 0 means case sensitve, 1 means case insensitive
2247 *
2248 * return 1 for found
2249 * return 0 for not found
2250 * return -1 for error (search out of bounds)
2251 */
uniSearchReal(const char * data,int dlen,PatternMatchData * pmd,int nocase)2252 static int uniSearchReal(const char *data, int dlen, PatternMatchData *pmd, int nocase)
2253 {
2254 /*
2255 * in theory computeDepth doesn't need to be called because the
2256 * depth + offset adjustments have been made by the calling function
2257 */
2258 int depth = dlen;
2259 int success = 0;
2260 const char *start_ptr = data;
2261 const char *end_ptr = data + dlen;
2262 const char *base_ptr = start_ptr;
2263 uint32_t extract_offset, extract_depth, extract_distance, extract_within;
2264 int search_start = 0;
2265
2266 if(pmd->use_doe != 1)
2267 {
2268 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "NOT Using Doe Ptr\n"););
2269 UpdateDoePtr(NULL, 0); /* get rid of all our pattern match state */
2270 }
2271
2272 /* Get byte_math/byte_extract variables */
2273 if (pmd->offset_var >= 0)
2274 {
2275 if(pmd->offset_var == BYTE_MATH_VAR_INDEX)
2276 {
2277 pmd->offset = (int32_t) bytemath_variable;
2278 }
2279 else if(pmd->offset_var == COMMON_VAR_INDEX)
2280 {
2281 pmd->offset = (int32_t) common_var;
2282 }
2283 else if (pmd->offset_var < NUM_BYTE_EXTRACT_VARS)
2284 {
2285 GetByteExtractValue(&extract_offset, pmd->offset_var);
2286 pmd->offset = (int32_t) extract_offset;
2287 }
2288 }
2289 if (pmd->depth_var >= 0)
2290 {
2291 if(pmd->depth_var == BYTE_MATH_VAR_INDEX)
2292 {
2293 pmd->depth = (int32_t) bytemath_variable;
2294 }
2295 else if(pmd->depth_var == COMMON_VAR_INDEX)
2296 {
2297 pmd->depth = (int32_t) common_var;
2298 }
2299 else if (pmd->depth_var < NUM_BYTE_EXTRACT_VARS)
2300 {
2301 GetByteExtractValue(&extract_depth, pmd->depth_var);
2302 pmd->depth = (int32_t) extract_depth;
2303 }
2304 }
2305 if (pmd->distance_var >= 0)
2306 {
2307 if(pmd->distance_var == BYTE_MATH_VAR_INDEX)
2308 {
2309 pmd->distance = (int32_t) bytemath_variable;
2310 }
2311 else if(pmd->distance == COMMON_VAR_INDEX)
2312 {
2313 pmd->distance = (int32_t) common_var;
2314 }
2315 else if (pmd->distance_var < NUM_BYTE_EXTRACT_VARS)
2316 {
2317 GetByteExtractValue(&extract_distance, pmd->distance_var);
2318 pmd->distance = (int32_t) extract_distance;
2319 }
2320 }
2321 if (pmd->within_var >= 0)
2322 {
2323 if(pmd->within_var == BYTE_MATH_VAR_INDEX)
2324 {
2325 pmd->within = (int32_t) bytemath_variable;
2326 }
2327 else if(pmd->within_var == COMMON_VAR_INDEX)
2328 {
2329 pmd->within = (int32_t) common_var;
2330 }
2331 else if (pmd->within_var < NUM_BYTE_EXTRACT_VARS)
2332 {
2333 GetByteExtractValue(&extract_within, pmd->within_var);
2334 pmd->within = (int32_t) extract_within;
2335 }
2336 }
2337
2338 // Set our initial starting point
2339 if (doe_ptr)
2340 {
2341 // Sanity check to make sure the doe_ptr is within the buffer we're
2342 // searching. It could be at the very end of the buffer due to a
2343 // previous match, but may have a negative distance here.
2344 if (((char *)doe_ptr < start_ptr) || ((char *)doe_ptr > end_ptr))
2345 {
2346 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Returning because "
2347 "doe_ptr isn't within the buffer we're searching: "
2348 "start_ptr: %p, end_ptr: %p, doe_ptr: %p\n",
2349 start_ptr, end_ptr, doe_ptr););
2350 return -1;
2351 }
2352
2353 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2354 "Setting base_ptr to doe_ptr (%p)\n", doe_ptr););
2355
2356 base_ptr = (const char *)doe_ptr;
2357 depth = dlen - ((char *)doe_ptr - data);
2358 }
2359 else
2360 {
2361 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2362 "Setting base_ptr to start_ptr (%p)\n", start_ptr););
2363
2364 base_ptr = start_ptr;
2365 depth = dlen;
2366 }
2367
2368 // Adjust base_ptr and depth based on distance/within
2369 // or offset/depth parameters.
2370 if ((pmd->distance != 0) || (pmd->within != PMD_WITHIN_UNDEFINED))
2371 {
2372 // This covers the pmd->distance > buffer case
2373 if (pmd->distance > depth)
2374 {
2375 depth = 0;
2376 }
2377 else if (pmd->distance != 0)
2378 {
2379 search_start = (base_ptr - start_ptr) + pmd->distance;
2380 base_ptr += pmd->distance;
2381 depth -= pmd->distance;
2382 }
2383
2384 // If the distance is negative and puts us before start_ptr
2385 // set base_ptr to start_ptr and adjust depth based on within.
2386 if (search_start < 0)
2387 {
2388 int delta = search_start;
2389 delta += (pmd->within == PMD_WITHIN_UNDEFINED) ? 0 : (int)pmd->within;
2390 // base_ptr is before start_ptr and the within is before start_ptr as well. Cannot re-adjust.
2391 if(delta < 0)
2392 return -1;
2393 base_ptr = start_ptr;
2394 depth = ((pmd->within == PMD_WITHIN_UNDEFINED) || (delta > dlen)) ? dlen : delta;
2395 }
2396 else if ((pmd->within != PMD_WITHIN_UNDEFINED) && ((int)pmd->within < depth))
2397 {
2398 depth = (int)pmd->within;
2399 }
2400 search_start = 0;
2401 }
2402 else if ((pmd->offset != 0) || (pmd->depth != 0))
2403 {
2404 if (pmd->offset > depth)
2405 {
2406 depth = 0;
2407 }
2408 else if (pmd->offset != 0)
2409 {
2410 search_start = pmd->offset;
2411 base_ptr += pmd->offset;
2412 depth -= pmd->offset;
2413 }
2414
2415 // If the distance is negative and puts us before start_ptr
2416 // set base_ptr to start_ptr and adjust depth based on pmd->depth.
2417 if (search_start < 0)
2418 {
2419 int delta = (int)pmd->depth + search_start;
2420 // base_ptr is before start_ptr and the depth is before start_ptr as well. Cannot re-adjust.
2421 if(delta < 0)
2422 return -1;
2423 base_ptr = start_ptr;
2424 depth = ((pmd->depth == 0) || (delta > dlen)) ? dlen : delta;
2425 }
2426 else if ((pmd->depth != 0) && (pmd->depth < depth))
2427 {
2428 depth = pmd->depth;
2429 }
2430 }
2431
2432 // If the pattern size is greater than the amount of data we have to
2433 // search, there's no way we can match, but return 0 here for the
2434 // case where the match is inverted and there is at least some data.
2435 if ((int)pmd->pattern_size > depth)
2436 {
2437 // The condition ((char *)doe_ptr == end_ptr) is for the corner case,
2438 // where the pattern match is exactly at the end of the payload and it
2439 // is a negated content match.
2440 if (pmd->exception_flag && (((char *)doe_ptr == end_ptr) || depth > 0))
2441 return 0;
2442
2443 return -1;
2444 }
2445
2446 #ifdef DEBUG_MSGS
2447 {
2448 char *hexbuf;
2449
2450 assert(depth <= dlen);
2451
2452 DebugMessage(DEBUG_PATTERN_MATCH, "uniSearchReal:\n ");
2453
2454 hexbuf = hex((u_char *)pmd->pattern_buf, pmd->pattern_size);
2455 DebugMessage(DEBUG_PATTERN_MATCH, " p->data: %p\n doe_ptr: %p\n "
2456 "base_ptr: %p\n depth: %d\n searching for: %s\n",
2457 data, doe_ptr, base_ptr, depth, hexbuf);
2458 free(hexbuf);
2459 }
2460 #endif /* DEBUG_MSGS */
2461
2462 if(nocase)
2463 {
2464 success = mSearchCI(base_ptr, depth,
2465 pmd->pattern_buf,
2466 pmd->pattern_size,
2467 pmd->skip_stride,
2468 pmd->shift_stride);
2469 }
2470 else
2471 {
2472 success = mSearch(base_ptr, depth,
2473 pmd->pattern_buf,
2474 pmd->pattern_size,
2475 pmd->skip_stride,
2476 pmd->shift_stride);
2477 }
2478
2479
2480 #ifdef DEBUG_MSGS
2481 if(success)
2482 {
2483 DebugMessage(DEBUG_PATTERN_MATCH, "matched, doe_ptr: %p (%d)\n",
2484 doe_ptr, ((char *)doe_ptr - data));
2485 }
2486 #endif
2487
2488 return success;
2489 }
2490
CheckANDPatternMatch(void * option_data,Packet * p)2491 int CheckANDPatternMatch(void *option_data, Packet *p)
2492 {
2493 int rval = DETECTION_OPTION_NO_MATCH;
2494 int found = -1;
2495 int dsize;
2496 const char *dp = NULL;
2497 #if 0
2498 int origUseDoe;
2499 char *orig_doe;
2500 #endif
2501 PatternMatchData *idx;
2502 PROFILE_VARS;
2503
2504 PREPROC_PROFILE_START(contentPerfStats);
2505
2506 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "CheckPatternANDMatch: "););
2507
2508 idx = (PatternMatchData *)option_data;
2509 #if 0
2510 origUseDoe = idx->use_doe;
2511 #endif
2512
2513 if(idx->rawbytes == 0)
2514 {
2515 if(Is_DetectFlag(FLAG_ALT_DETECT))
2516 {
2517 dsize = DetectBuffer.len;
2518 dp = (const char*) DetectBuffer.data;
2519 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2520 "Using Alternative Detect buffer!\n"););
2521 }
2522 else if(Is_DetectFlag(FLAG_ALT_DECODE))
2523 {
2524 dsize = DecodeBuffer.len;
2525 dp = (const char *) DecodeBuffer.data;
2526 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2527 "Using Alternative Decode buffer!\n"););
2528 }
2529 else
2530 {
2531 if(IsLimitedDetect(p))
2532 {
2533 dsize = p->alt_dsize;
2534 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2535 "Using Limited Packet Data!\n"););
2536 }
2537 else
2538 {
2539 dsize = p->dsize;
2540 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2541 "Using Full Packet Data!\n"););
2542 }
2543 dp = (const char *) p->data;
2544 }
2545 }
2546 else
2547 {
2548 dsize = p->dsize;
2549 dp = (const char *) p->data;
2550 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2551 "Using Full Packet Data!\n"););
2552 }
2553
2554 #if 0
2555 /* this now takes care of all the special cases where we'd run
2556 * over the buffer */
2557 orig_doe = (char *)doe_ptr;
2558 #endif
2559
2560 if(doe_buf_flags & DOE_BUF_URI)
2561 UpdateDoePtr(NULL, 0);
2562
2563 doe_buf_flags = DOE_BUF_STD;
2564
2565 #ifndef NO_FOUND_ERROR
2566 if( p->dsize != 0 )
2567 {
2568 found = idx->search(dp, dsize, idx);
2569 }
2570 if ( found == -1 )
2571 {
2572 /* On error, mark as not found. This is necessary to handle !content
2573 cases. In that case, a search that is outside the given buffer will
2574 return 0, and !0 is 1, so a !content out of bounds will return true,
2575 which is not what we want. */
2576 found = 0;
2577 }
2578 else
2579 {
2580 found ^= idx->exception_flag;
2581 }
2582 #else
2583 /* Original code. Does not account for searching outside the buffer. */
2584 found = (idx->search(dp, dsize, idx) ^ idx->exception_flag);
2585 #endif
2586
2587 if ( found )
2588 {
2589 if ( idx->replace_buf && !PacketWasCooked(p) )
2590 {
2591 //fix the packet buffer to have the new string
2592 int detect_depth = (char *)doe_ptr - idx->pattern_size - dp;
2593
2594 if (detect_depth < 0)
2595 {
2596 PREPROC_PROFILE_END(contentPerfStats);
2597 return rval;
2598 }
2599 Replace_StoreOffset(idx, detect_depth);
2600 }
2601 rval = DETECTION_OPTION_MATCH;
2602 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Pattern match found\n"););
2603 }
2604 else
2605 {
2606 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Pattern match failed\n"););
2607 }
2608 #if 0
2609 while (found)
2610 {
2611 /* save where we last did the pattern match */
2612 tmp_doe = (char *)doe_ptr;
2613
2614 /* save start doe as beginning of this pattern + non-repeating length*/
2615 start_doe = (char *)doe_ptr - idx->pattern_size + idx->pattern_max_jump_size;
2616
2617 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Pattern Match successful!\n"););
2618 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Check next functions!\n"););
2619 /* PROFILING Don't count rest of options towards content */
2620 PREPROC_PROFILE_TMPEND(contentPerfStats);
2621
2622 /* Try evaluating the rest of the rules chain */
2623 next_found= fp_list->next->OptTestFunc(p, otn_idx, fp_list->next);
2624
2625 /* PROFILING Don't count rest of options towards content */
2626 PREPROC_PROFILE_TMPSTART(contentPerfStats);
2627
2628 if(next_found != 0)
2629 {
2630 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2631 "Next functions matched!\n"););
2632
2633 /* We found a successful match, return that this rule has fired off */
2634 PREPROC_PROFILE_END(contentPerfStats);
2635 return next_found;
2636 }
2637 else if(tmp_doe != NULL)
2638 {
2639 int new_dsize = dsize-(start_doe-dp);
2640
2641 /* if the next option isn't relative and it failed, we're done */
2642 if (fp_list->next->isRelative == 0)
2643 {
2644 PREPROC_PROFILE_END(contentPerfStats);
2645 return 0;
2646 }
2647
2648 if(new_dsize <= 0 || new_dsize > dsize)
2649 {
2650 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2651 "The new dsize is less than <= 0 or > "
2652 "the the original dsize;returning "
2653 "false\n"););
2654 idx->use_doe = origUseDoe;
2655 PREPROC_PROFILE_END(contentPerfStats);
2656 return 0;
2657 }
2658
2659 if (orig_doe)
2660 {
2661 /* relative to a previously found pattern */
2662 if (((idx->distance != 0) && (start_doe - orig_doe > idx->distance)) ||
2663 ((idx->offset != 0) && (start_doe - orig_doe > idx->offset)) )
2664 {
2665 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2666 "The next starting point to search "
2667 "from is beyond the original "
2668 "distance;returning false\n"););
2669 idx->use_doe = origUseDoe;
2670 PREPROC_PROFILE_END(contentPerfStats);
2671 return 0;
2672 }
2673
2674 if (((idx->within != 0) &&
2675 (start_doe - orig_doe + idx->pattern_size > (unsigned int)idx->within)) ||
2676 ((idx->depth != 0) &&
2677 (start_doe - orig_doe + idx->pattern_size > (unsigned int)idx->depth)) )
2678 {
2679 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2680 "The next starting point to search "
2681 "from is beyond the original "
2682 "within;returning false\n"););
2683 idx->use_doe = origUseDoe;
2684 PREPROC_PROFILE_END(contentPerfStats);
2685 return 0;
2686 }
2687 }
2688 else
2689 {
2690 /* relative to beginning of data */
2691 if (((idx->distance != 0) && (start_doe - dp > idx->distance)) ||
2692 ((idx->offset != 0) && (start_doe - dp > idx->offset)) )
2693 {
2694 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2695 "The next starting point to search "
2696 "from is beyond the original "
2697 "distance;returning false\n"););
2698 idx->use_doe = origUseDoe;
2699 PREPROC_PROFILE_END(contentPerfStats);
2700 return 0;
2701 }
2702
2703 if (((idx->within != 0) &&
2704 (start_doe - dp + idx->pattern_size > (unsigned int)idx->within)) ||
2705 ((idx->depth != 0) &&
2706 (start_doe - dp + idx->pattern_size > (unsigned int)idx->depth)) )
2707 {
2708 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2709 "The next starting point to search "
2710 "from is beyond the original "
2711 "within;returning false\n"););
2712 idx->use_doe = origUseDoe;
2713 PREPROC_PROFILE_END(contentPerfStats);
2714 return 0;
2715 }
2716 }
2717
2718 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2719 "At least ONE of the next functions does to match!\n"););
2720 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2721 "Start search again from a next point!\n"););
2722
2723 /* Start the search again from the last set of contents, with a new depth and dsize */
2724 doe_ptr = (uint8_t *)start_doe;
2725 idx->use_doe = 1;
2726 found = (idx->search(start_doe, new_dsize,idx) ^ idx->exception_flag);
2727
2728 /*
2729 ** If we haven't updated doe since we set it at the beginning
2730 ** of the loop, then that means we have already done the exact
2731 ** same search previously, and have nothing else to gain from
2732 ** doing the same search again.
2733 */
2734 if(start_doe == (char *)doe_ptr)
2735 {
2736 idx->use_doe = origUseDoe;
2737 PREPROC_PROFILE_END(contentPerfStats);
2738 return 0;
2739 }
2740 }
2741 else
2742 {
2743 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2744 "Returning 0 because tmp_doe is NULL\n"););
2745
2746 idx->use_doe = origUseDoe;
2747 PREPROC_PROFILE_END(contentPerfStats);
2748 return 0;
2749 }
2750
2751 }
2752 #endif
2753
2754 //idx->use_doe = origUseDoe;
2755 PREPROC_PROFILE_END(contentPerfStats);
2756 return rval;
2757 }
2758
CheckUriPatternMatch(void * option_data,Packet * p)2759 int CheckUriPatternMatch(void *option_data, Packet *p)
2760 {
2761 int rval = DETECTION_OPTION_NO_MATCH;
2762 int found = 0;
2763 PatternMatchData *idx = (PatternMatchData *)option_data;
2764 const HttpBuffer* hb = GetHttpBuffer(idx->http_buffer);
2765 PROFILE_VARS;
2766
2767 if ( !hb )
2768 {
2769 DEBUG_WRAP(DebugMessage(DEBUG_HTTP_DECODE,"CheckUriPatternMatch: no "
2770 "HTTP buffers set, retuning"););
2771 return rval;
2772 }
2773
2774 PREPROC_PROFILE_START(uricontentPerfStats);
2775
2776 /*
2777 * have to reset the doe_ptr for each new UriBuf
2778 */
2779 if(idx->use_doe != 1)
2780 UpdateDoePtr(NULL, 0);
2781
2782 else if(!(doe_buf_flags & DOE_BUF_URI))
2783 SetDoePtr(hb->buf, DOE_BUF_URI);
2784
2785 /* this now takes care of all the special cases where we'd run
2786 * over the buffer */
2787 found = idx->search((const char *)hb->buf, hb->length, idx);
2788
2789 if (found == -1)
2790 found = 0;
2791 else
2792 found ^= idx->exception_flag;
2793
2794 if(found > 0 )
2795 {
2796 doe_buf_flags = DOE_BUF_URI;
2797 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Pattern Match successful!\n"););
2798
2799 /* call the next function in the OTN */
2800 PREPROC_PROFILE_END(uricontentPerfStats);
2801 return DETECTION_OPTION_MATCH;
2802 }
2803
2804 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Pattern match failed\n"););
2805 PREPROC_PROFILE_END(uricontentPerfStats);
2806 return rval;
2807 }
2808
PatternMatchDuplicatePmd(void * src,PatternMatchData * pmd_dup)2809 void PatternMatchDuplicatePmd(void *src, PatternMatchData *pmd_dup)
2810 {
2811 /* Oh, C++ where r u? can't we have a friggin' copy constructor? */
2812 PatternMatchData *pmd_src = (PatternMatchData *)src;
2813 if (!pmd_src || !pmd_dup)
2814 return;
2815
2816 pmd_dup->exception_flag = pmd_src->exception_flag;
2817 pmd_dup->offset = pmd_src->offset;
2818 pmd_dup->depth = pmd_src->depth;
2819 pmd_dup->distance = pmd_src->distance;
2820 pmd_dup->within = pmd_src->within;
2821 pmd_dup->offset_var = pmd_src->offset_var;
2822 pmd_dup->depth_var = pmd_src->depth_var;
2823 pmd_dup->distance_var = pmd_src->distance_var;
2824 pmd_dup->within_var = pmd_src->within_var;
2825 pmd_dup->rawbytes = pmd_src->rawbytes;
2826 pmd_dup->nocase = pmd_src->nocase;
2827 pmd_dup->use_doe = pmd_src->use_doe;
2828 pmd_dup->http_buffer = pmd_src->http_buffer;
2829 pmd_dup->buffer_func = pmd_src->buffer_func;
2830 pmd_dup->pattern_size = pmd_src->pattern_size;
2831 pmd_dup->replace_size = pmd_src->replace_size;
2832 pmd_dup->replace_buf = pmd_src->replace_buf;
2833 pmd_dup->pattern_buf = pmd_src->pattern_buf;
2834 pmd_dup->search = pmd_src->search;
2835 pmd_dup->skip_stride = pmd_src->skip_stride;
2836 pmd_dup->shift_stride = pmd_src->shift_stride;
2837 pmd_dup->pattern_max_jump_size = pmd_src->pattern_max_jump_size;
2838 pmd_dup->fp = pmd_src->fp;
2839 pmd_dup->fp_only = pmd_src->fp_only;
2840 pmd_dup->fp_offset = pmd_src->fp_offset;
2841 pmd_dup->fp_length = pmd_src->fp_length;
2842 pmd_dup->pattern_type = pmd_src->pattern_type;
2843 pmd_dup->protected_pattern = pmd_src->protected_pattern;
2844 pmd_dup->protected_length = pmd_src->protected_length;
2845
2846 pmd_dup->last_check.ts.tv_sec = pmd_src->last_check.ts.tv_sec;
2847 pmd_dup->last_check.ts.tv_usec = pmd_src->last_check.ts.tv_usec;
2848 pmd_dup->last_check.packet_number = pmd_src->last_check.packet_number;
2849 pmd_dup->last_check.rebuild_flag = pmd_src->last_check.rebuild_flag;
2850
2851 pmd_dup->prev = NULL;
2852 pmd_dup->next = NULL;
2853 pmd_dup->fpl = NULL;
2854
2855 Replace_ResetOffset(pmd_dup);
2856 }
2857
2858 /* current_cursor should be the doe_ptr after this content rule option matched
2859 * orig_cursor is the place from where we first did evaluation of this content */
PatternMatchAdjustRelativeOffsets(PatternMatchData * orig_pmd,PatternMatchData * dup_pmd,const uint8_t * current_cursor,const uint8_t * orig_cursor)2860 int PatternMatchAdjustRelativeOffsets(PatternMatchData *orig_pmd, PatternMatchData *dup_pmd,
2861 const uint8_t *current_cursor, const uint8_t *orig_cursor)
2862 {
2863 /* Adjust for repeating patterns, e.g. ABAB
2864 * This is where the new search for this content should start */
2865 const uint8_t *start_cursor =
2866 (current_cursor - dup_pmd->pattern_size) + dup_pmd->pattern_max_jump_size;
2867
2868 if (orig_pmd->depth != 0)
2869 {
2870 /* This was relative to a previously found pattern. No space left to
2871 * search, we're done */
2872 if ((start_cursor + dup_pmd->pattern_size)
2873 > (orig_cursor + dup_pmd->offset + dup_pmd->depth))
2874 {
2875 return 0;
2876 }
2877
2878 /* Adjust offset and depth to reflect new position */
2879 /* Lop off what we used */
2880 dup_pmd->depth -= start_cursor - (orig_cursor + dup_pmd->offset);
2881 /* Make offset where we will start the next search */
2882 dup_pmd->offset = start_cursor - orig_cursor;
2883 }
2884 else if (orig_pmd->within != PMD_WITHIN_UNDEFINED)
2885 {
2886 /* This was relative to a previously found pattern. No space left to
2887 * search, we're done */
2888 if ((start_cursor + dup_pmd->pattern_size)
2889 > (orig_cursor + dup_pmd->distance + dup_pmd->within))
2890 {
2891 return 0;
2892 }
2893
2894 /* Adjust distance and within to reflect new position */
2895 /* Lop off what we used */
2896 dup_pmd->within -= start_cursor - (orig_cursor + dup_pmd->distance);
2897 /* Make distance where we will start the next search */
2898 dup_pmd->distance = start_cursor - orig_cursor;
2899 }
2900 else if (orig_pmd->use_doe)
2901 {
2902 dup_pmd->distance = start_cursor - orig_cursor;
2903 }
2904 else
2905 {
2906 dup_pmd->offset = start_cursor - orig_cursor;
2907 }
2908
2909 return 1;
2910 }
2911
2912 #if 0
2913 /* Not currently in use - DO NOT REMOVE */
2914 static inline int computeDepth(int dlen, PatternMatchData * pmd)
2915 {
2916 /* do some tests to make sure we stay in bounds */
2917 if((pmd->depth + pmd->offset) > dlen)
2918 {
2919 /* we want to check only depth bytes anyway */
2920 int sub_depth = dlen - pmd->offset;
2921
2922 if((sub_depth > 0) && (sub_depth >= (int)pmd->pattern_size))
2923 {
2924 return sub_depth;
2925 }
2926 else
2927 {
2928 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
2929 "Pattern Match failed -- sub_depth: %d < "
2930 "(int)pmd->pattern_size: %d!\n",
2931 sub_depth, (int)pmd->pattern_size););
2932
2933 return -1;
2934 }
2935 }
2936 else
2937 {
2938 if(pmd->depth && (dlen - pmd->offset > pmd->depth))
2939 {
2940 return pmd->depth;
2941 }
2942 else
2943 {
2944 return dlen - pmd->offset;
2945 }
2946 }
2947 }
2948
2949 static int uniSearchREG(char * data, int dlen, PatternMatchData * pmd)
2950 {
2951 int depth = computeDepth(dlen, pmd);
2952 /* int distance_adjustment = 0;
2953 * int depth_adjustment = 0;
2954 */
2955 int success = 0;
2956
2957 if (depth < 0)
2958 return 0;
2959
2960 /* XXX DESTROY ME */
2961 /*success = mSearchREG(data + pmd->offset + distance_adjustment,
2962 depth_adjustment!=0?depth_adjustment:depth,
2963 pmd->pattern_buf, pmd->pattern_size, pmd->skip_stride,
2964 pmd->shift_stride);*/
2965
2966 return success;
2967 }
2968 #endif
2969
2970 #if 0
2971 /* XXX Not completetly implemented */
2972 static void PayloadSearchListInit(char *data, OptTreeNode * otn, int protocol)
2973 {
2974 char *sptr;
2975 char *eptr;
2976
2977 lastType = PLUGIN_PATTERN_MATCH_OR;
2978
2979 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "In PayloadSearchListInit()\n"););
2980
2981 /* get the path/file name from the data */
2982 while(isspace((int) *data))
2983 data++;
2984
2985 /* grab everything between the starting " and the end one */
2986 sptr = strchr(data, '"');
2987 eptr = strrchr(data, '"');
2988
2989 if(sptr != NULL && eptr != NULL)
2990 {
2991 /* increment past the first quote */
2992 sptr++;
2993
2994 /* zero out the second one */
2995 *eptr = 0;
2996 }
2997 else
2998 {
2999 sptr = data;
3000 }
3001
3002 /* read the content keywords from the list file */
3003 ParseContentListFile(sptr, otn, protocol);
3004
3005 /* link the plugin function in to the current OTN */
3006 AddOptFuncToList(CheckORPatternMatch, otn);
3007
3008 return;
3009 }
3010
3011 /****************************************************************************
3012 *
3013 * Function: ParseContentListFile(char *, OptTreeNode *, int protocol)
3014 *
3015 * Purpose: Read the content_list file a line at a time, put the content of
3016 * the line into buffer
3017 *
3018 * Arguments:otn => rule including the list
3019 * file => list file filename
3020 * protocol => protocol
3021 *
3022 * Returns: void function
3023 *
3024 ***************************************************************************/
3025 static void ParseContentListFile(char *file, OptTreeNode * otn, int protocol)
3026 {
3027 FILE *thefp; /* file pointer for the content_list file */
3028 char buf[STD_BUF+1]; /* file read buffer */
3029 char rule_buf[STD_BUF+1]; /* content keyword buffer */
3030 int frazes_count; /* frazes counter */
3031
3032
3033 #ifdef DEBUG_MSGS
3034 PatternMatchData *idx;
3035 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Opening content_list file: %s\n", file););
3036 #endif /* DEBUG_MSGS */
3037 /* open the list file */
3038 thefp = fopen(file, "r");
3039 if (thefp == NULL)
3040 {
3041 ParseError("Unable to open list file: %s", file);
3042 }
3043
3044 /* clear the line and rule buffers */
3045 memset((char *) buf, 0, STD_BUF);
3046 memset((char *) rule_buf, 0, STD_BUF);
3047 frazes_count = 0;
3048
3049 /* loop thru each list_file line and content to the rule */
3050 while((fgets(buf, STD_BUF-2, thefp)) != NULL)
3051 {
3052 /* inc the line counter */
3053 list_file_line++;
3054
3055 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Got line %d: %s",
3056 list_file_line, buf););
3057
3058 /* if it's not a comment or a <CR>, send it to the parser */
3059 if((buf[0] != '#') && (buf[0] != 0x0a) && (buf[0] != ';'))
3060 {
3061 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3062 "Adding content keyword: %s", buf););
3063
3064 frazes_count++;
3065 strip(buf);
3066
3067 NewNode(otn, PLUGIN_PATTERN_MATCH_OR);
3068
3069 /* check and add content keyword */
3070 ParsePattern(buf, otn, PLUGIN_PATTERN_MATCH_OR);
3071
3072 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3073 "Content keyword %s\" added!\n", buf););
3074 }
3075 }
3076 #ifdef DEBUG_MSGS
3077 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "%d frazes read...\n", frazes_count););
3078 idx = (PatternMatchData *) otn->ds_list[PLUGIN_PATTERN_MATCH_OR];
3079
3080 if(idx == NULL)
3081 {
3082 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "No patterns loaded\n"););
3083 }
3084 else
3085 {
3086 while(idx != NULL)
3087 {
3088 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Pattern = %s\n",
3089 idx->pattern_buf););
3090 idx = idx->next;
3091 }
3092 }
3093 #endif /* DEBUG_MSGS */
3094
3095 fclose(thefp);
3096
3097 return;
3098 }
3099
3100 int CheckORPatternMatch(Packet * p, OptTreeNode * otn_idx, OptFpList * fp_list)
3101 {
3102 int found = 0;
3103 int dsize;
3104 char *dp;
3105
3106
3107 PatternMatchData *idx;
3108
3109 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "CheckPatternORMatch: "););
3110
3111 idx = otn_idx->ds_list[PLUGIN_PATTERN_MATCH_OR];
3112
3113 while(idx != NULL)
3114 {
3115 if (Is_DetectFlag(FLAG_ALT_DETECT) && (idx->rawbytes == 0))
3116 {
3117 dsize = DetectBuffer.len;
3118 dp = (char *)DetectBufffer.data;
3119 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3120 "Using Alternative Detect buffer!\n"););
3121 }
3122 else if(Is_DetectFlag(FLAG_ALT_DECODE) && (idx->rawbytes == 0))
3123 {
3124 dsize = DecodeBuffer.len;
3125 dp = (char *) DecodeBuffer.data;
3126 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3127 "Using Alternative Decode buffer!\n"););
3128 }
3129 else
3130 {
3131 if(IsLimitedDetect(p))
3132 dsize = p->alt_dsize;
3133 else
3134 dsize = p->dsize;
3135 dp = (char *) p->data;
3136 }
3137
3138
3139 if(idx->offset > dsize)
3140 {
3141 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3142 "Initial offset larger than payload!\n"););
3143
3144 goto sizetoosmall;
3145 }
3146 else
3147 {
3148 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3149 "testing pattern: %s\n", idx->pattern_buf););
3150 found = idx->search(dp, dsize, idx);
3151
3152 if(!found)
3153 {
3154 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3155 "Pattern Match failed!\n"););
3156 }
3157 }
3158
3159 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3160 "Checking the results\n"););
3161
3162 if(found)
3163 {
3164 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Pattern Match "
3165 "successful: %s!\n", idx->pattern_buf););
3166
3167 return fp_list->next->OptTestFunc(p, otn_idx, fp_list->next);
3168
3169 }
3170 else
3171 {
3172 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3173 "Pattern match failed\n"););
3174 }
3175
3176 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3177 "Stepping to next content keyword\n"););
3178
3179 sizetoosmall:
3180
3181 idx = idx->next;
3182 }
3183
3184 DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
3185 "No more keywords, exiting... \n"););
3186
3187 return 0;
3188 }
3189 #endif
3190
3191