1 /*
2 ** $Id$
3
4 ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
5 ** Copyright (C) 2003-2013 Sourcefire, Inc.
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 * Major rewrite: Hui Cao <hcao@sourcefire.com>
24 *
25 * Add flowbits OR support
26 *
27 **
28 ** sp_flowbits
29 **
30 ** Purpose:
31 **
32 ** Wouldn't it be nice if we could do some simple state tracking
33 ** across multiple packets? Well, this allows you to do just that.
34 **
35 ** Effect:
36 **
37 ** - [Un]set a bitmask stored with the session
38 ** - Check the value of the bitmask
39 **
40 *
41 */
42
43 #include <stdlib.h>
44 #include <string.h>
45 #include <ctype.h>
46 #include <sys/types.h>
47
48 #ifdef HAVE_CONFIG_H
49 #include "config.h"
50 #endif
51
52 #include "sf_types.h"
53 #include "rules.h"
54 #include "treenodes.h"
55 #include "decode.h"
56 #include "plugbase.h"
57 #include "parser.h"
58 #include "snort_debug.h"
59 #include "util.h"
60 #include "plugin_enum.h"
61 #include "snort.h"
62 #include "bitop_funcs.h"
63 #include "sfghash.h"
64 #include "sp_flowbits.h"
65 #include "sf_types.h"
66 #include "mstring.h"
67
68 #include "session_api.h"
69 #include "stream_api.h"
70
71 #include "snort.h"
72 #include "profiler.h"
73 #ifdef PERF_PROFILING
74 PreprocStats flowBitsPerfStats;
75 extern PreprocStats ruleOTNEvalPerfStats;
76 #endif
77
78 #include "sfhashfcn.h"
79 #include "detection_options.h"
80 #define DEFAULT_FLOWBIT_GROUP "default"
81
82 #define ALLOWED_SPECIAL_CHARS ".-_"
83
84 SFGHASH *flowbits_hash = NULL;
85 SFGHASH *flowbits_grp_hash = NULL;
86 SF_QUEUE *flowbits_bit_queue = NULL;
87 uint32_t flowbits_count = 0;
88 uint32_t flowbits_grp_count = 0;
89 int flowbits_toggle = 1;
90
91 #define DEFAULT_FLOWBIT_SIZE 1024
92 #define MAX_FLOWBIT_SIZE 2048
93 #define CONVERT_BITS_TO_BYTES(size) ( (size > 1)?(((size -1) >> 3)+ 1):0)
94 static unsigned int giFlowbitSizeInBytes = CONVERT_BITS_TO_BYTES(DEFAULT_FLOWBIT_SIZE);
95 static unsigned int giFlowbitSize = DEFAULT_FLOWBIT_SIZE;
96
97 void FlowItemFree(void *);
98 void FlowBitsGrpFree(void *);
99 static void FlowBitsInit(struct _SnortConfig *, char *, OptTreeNode *, int);
100 static void FlowBitsParse(struct _SnortConfig *, char *, FLOWBITS_OP *, OptTreeNode *);
101 static void FlowBitsCleanExit(int, void *);
102
103 #ifdef SNORT_RELOAD
104 extern volatile bool reloadInProgress;
105 #endif
106
107 /****************************************************************************
108 *
109 * Function: FlowBitsHashInit(void)
110 *
111 * Purpose: Initialize the hash table and queue storage for flowbits IDs
112 *
113 * Arguments: None
114 *
115 * Returns: void function
116 *
117 ****************************************************************************/
FlowBitsHashInit(void)118 void FlowBitsHashInit(void)
119 {
120 if (flowbits_hash != NULL)
121 return;
122
123 flowbits_hash = sfghash_new(10000, 0, 0, FlowItemFree);
124 if (flowbits_hash == NULL)
125 {
126 FatalError("%s(%d) Could not create flowbits hash.\n",
127 __FILE__, __LINE__);
128 }
129
130
131 flowbits_bit_queue = sfqueue_new();
132 if (flowbits_bit_queue == NULL)
133 {
134 FatalError("%s(%d) Could not create flowbits bit queue.\n",
135 __FILE__, __LINE__);
136 }
137 }
138
FlowBitsGrpHashInit(void)139 void FlowBitsGrpHashInit(void)
140 {
141 if (flowbits_grp_hash != NULL)
142 return;
143
144 flowbits_grp_hash = sfghash_new(10000, 0, 0, FlowBitsGrpFree);
145 if (flowbits_grp_hash == NULL)
146 {
147 FatalError("%s(%d) Could not create flowbits group hash.\n",
148 __FILE__, __LINE__);
149 }
150
151 }
152
FlowItemFree(void * d)153 void FlowItemFree(void *d)
154 {
155 FLOWBITS_OBJECT *data = (FLOWBITS_OBJECT *)d;
156 free(data);
157 }
158
FlowBitsGrpFree(void * d)159 void FlowBitsGrpFree(void *d)
160 {
161 FLOWBITS_GRP *data = (FLOWBITS_GRP *)d;
162 boFreeBITOP(&(data->GrpBitOp));
163 if (data->name)
164 free(data->name);
165 free(data);
166 }
167
FlowBitsFree(void * d)168 void FlowBitsFree(void *d)
169 {
170 FLOWBITS_OP *data = (FLOWBITS_OP *)d;
171 if (data->ids)
172 free(data->ids);
173 if (data->name)
174 free(data->name);
175 if (data->group)
176 free(data->group);
177 free(data);
178 }
179
FlowBitsHash(void * d)180 uint32_t FlowBitsHash(void *d)
181 {
182 uint32_t a,b,c;
183 FLOWBITS_OP *data = (FLOWBITS_OP *)d;
184 int i;
185 int j = 0;
186
187 a = data->eval;
188 b = data->type;
189 c = RULE_OPTION_TYPE_FLOWBIT;
190
191 mix(a,b,c);
192
193 for (i = 0, j = 0; i < data->num_ids; i++, j++)
194 {
195 if (j >= 3)
196 {
197 a += data->ids[i - 2];
198 b += data->ids[i - 1];
199 c += data->ids[i];
200 mix(a,b,c);
201 j -= 3;
202 }
203 }
204 if (1 == j)
205 {
206 a += data->ids[data->num_ids - 1];
207 b += data->num_ids;
208 }
209 else if (2 == j)
210 {
211 a += data->ids[data->num_ids - 2];
212 b += data->ids[data->num_ids - 1]|data->num_ids << 16;
213 }
214
215 c += data->group_id;
216
217 final(a,b,c);
218
219 return c;
220 }
221
FlowBitsCompare(void * l,void * r)222 int FlowBitsCompare(void *l, void *r)
223 {
224 FLOWBITS_OP *left = (FLOWBITS_OP *)l;
225 FLOWBITS_OP *right = (FLOWBITS_OP *)r;
226 int i;
227
228 if (!left || !right)
229 return DETECTION_OPTION_NOT_EQUAL;
230
231 if ((left->num_ids != right->num_ids)||
232 (left->eval != right->eval)||
233 (left->type != right->type)||
234 (left->group_id != right->group_id))
235 return DETECTION_OPTION_NOT_EQUAL;
236
237
238 for (i = 0; i < left->num_ids; i++)
239 {
240 if (left->ids[i] != right->ids[i])
241
242 return DETECTION_OPTION_NOT_EQUAL;
243 }
244
245 return DETECTION_OPTION_EQUAL;
246 }
247
248 /****************************************************************************
249 *
250 * Function: SetupFlowBits()
251 *
252 * Purpose: Generic detection engine plugin template. Registers the
253 * configuration function and links it to a rule keyword. This is
254 * the function that gets called from InitPlugins in plugbase.c.
255 *
256 * Arguments: None.
257 *
258 * Returns: void function
259 *
260 * 3/4/05 - man beefed up the hash table size from 100 -> 10000
261 *
262 ****************************************************************************/
SetupFlowBits(void)263 void SetupFlowBits(void)
264 {
265 /* map the keyword to an initialization/processing function */
266 RegisterRuleOption("flowbits", FlowBitsInit, NULL, OPT_TYPE_DETECTION, NULL);
267
268 #ifdef PERF_PROFILING
269 RegisterPreprocessorProfile("flowbits", &flowBitsPerfStats, 3, &ruleOTNEvalPerfStats, NULL);
270 #endif
271
272 AddFuncToCleanExitList(FlowBitsCleanExit, NULL);
273
274 DEBUG_WRAP(DebugMessage(DEBUG_FLOWBITS, "Plugin: FlowBits Setup\n"););
275 }
276
277
278 /****************************************************************************
279 *
280 * Function: FlowBitsInit(struct _SnortConfig *, char *, OptTreeNode *)
281 *
282 * Purpose: Configure the flow init option to register the appropriate checks
283 *
284 * Arguments: data => rule arguments/data
285 * otn => pointer to the current rule option list node
286 *
287 * Returns: void function
288 *
289 ****************************************************************************/
FlowBitsInit(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)290 static void FlowBitsInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
291 {
292 FLOWBITS_OP *flowbits;
293 OptFpList *fpl;
294 void *idx_dup;
295
296 /* Flowbits are now part of the rule stub for .so rules.
297 * We avoid adding the flowbit twice by skipping it here. */
298 if (otn->sigInfo.generator == 3)
299 return;
300
301 /* Flow bits are handled by Stream if its enabled */
302 if( stream_api && stream_api->version != STREAM_API_VERSION5)
303 {
304 if (ScConfErrorOut())
305 {
306 FatalError("WARNING: %s (%d) => flowbits without Stream. "
307 "Stream must be enabled for this plugin.\n",
308 file_name,file_line);
309 }
310 else
311 {
312 LogMessage("WARNING: %s (%d) => flowbits without Stream. "
313 "Stream must be enabled for this plugin.\n",
314 file_name,file_line);
315 }
316 }
317
318 /* Auto init hash table and queue */
319 if (flowbits_hash == NULL)
320 FlowBitsHashInit();
321
322 if (flowbits_grp_hash == NULL )
323 FlowBitsGrpHashInit();
324
325 flowbits = (FLOWBITS_OP *) SnortAlloc(sizeof(FLOWBITS_OP));
326 if (!flowbits) {
327 FatalError("%s (%d): Unable to allocate flowbits node\n", file_name,
328 file_line);
329 }
330
331 /* Set the ds_list value to 1 (yes, we have flowbits for this rule) */
332 otn->ds_list[PLUGIN_FLOWBIT] = (void *)1;
333
334 FlowBitsParse(sc, data, flowbits, otn);
335 if (add_detection_option(sc, RULE_OPTION_TYPE_FLOWBIT, (void *)flowbits, &idx_dup) == DETECTION_OPTION_EQUAL)
336 {
337 char *group_name = ((FLOWBITS_OP *)idx_dup)->group;
338
339 #ifdef DEBUG_RULE_OPTION_TREE
340 LogMessage("Duplicate FlowBit:\n%d %c\n%d %c\n\n",
341 flowbits->id,
342 flowbits->type,
343 ((FLOWBITS_OP *)idx_dup)->id,
344 ((FLOWBITS_OP *)idx_dup)->type);
345 #endif
346 if (flowbits->group)
347 {
348 if (group_name && strcmp(group_name, flowbits->group))
349 free(group_name);
350 ((FLOWBITS_OP *)idx_dup)->group = SnortStrdup(flowbits->group);
351 }
352
353 free(flowbits->ids);
354 free(flowbits->name);
355 free(flowbits->group);
356 free(flowbits);
357 flowbits = idx_dup;
358 }
359
360 fpl = AddOptFuncToList(FlowBitsCheck, otn);
361 fpl->type = RULE_OPTION_TYPE_FLOWBIT;
362
363 /*
364 * attach it to the context node so that we can call each instance
365 * individually
366 */
367
368 fpl->context = (void *) flowbits;
369 return;
370 }
371
udpateFlowBitGroupInfo(FLOWBITS_GRP * flowbits_grp,char * flowbitName,FLOWBITS_OBJECT * flowbits_item)372 static void udpateFlowBitGroupInfo(FLOWBITS_GRP *flowbits_grp, char *flowbitName,
373 FLOWBITS_OBJECT *flowbits_item)
374 {
375
376 char *groupName;
377
378 if (!flowbits_grp)
379 return;
380
381 groupName = flowbits_grp->name;
382
383 if (!groupName)
384 return;
385
386 flowbits_grp->count++;
387 if ( flowbits_grp->max_id < flowbits_item->id )
388 flowbits_grp->max_id = flowbits_item->id;
389 boSetBit(&(flowbits_grp->GrpBitOp),flowbits_item->id);
390
391 }
392
validateName(char * name)393 static bool validateName(char *name)
394 {
395 unsigned i;
396
397 if (!name)
398 return false;
399
400 for (i=0; i<strlen(name); i++)
401 {
402 if (!isalnum(name[i])&&(NULL == strchr(ALLOWED_SPECIAL_CHARS,name[i])))
403 return false;
404 }
405 return true;
406 }
407 /****************************************************************************
408 *
409 * Function: getFlowBitItem(char *, FLOWBITS_OP *, FLOWBITS_GRP *)
410 *
411 * Purpose: update the flowbits
412 *
413 * Arguments:
414 * char *flowbit --> flowbits name,
415 * FLOWBITS_OP *flowbits
416 * FLOWBITS_GRP *flowbits_grp
417 *
418 * Returns: FLOWBITS_OBJECT*, whether exist before
419 *
420 ****************************************************************************/
getFlowBitItem(char * flowbitName,FLOWBITS_OP * flowbits,FLOWBITS_GRP * flowbits_grp)421 static FLOWBITS_OBJECT* getFlowBitItem(char *flowbitName, FLOWBITS_OP *flowbits, FLOWBITS_GRP *flowbits_grp)
422 {
423 FLOWBITS_OBJECT *flowbits_item;
424 int hstatus;
425
426 if (!validateName(flowbitName))
427 {
428 ParseError("Flowbits: flowbits name is limited to any alphanumeric string including %s"
429 , ALLOWED_SPECIAL_CHARS);
430 }
431
432 flowbits_item = (FLOWBITS_OBJECT *)sfghash_find(flowbits_hash, flowbitName);
433
434 if (flowbits_item == NULL)
435 {
436 flowbits_item = (FLOWBITS_OBJECT *)SnortAlloc(sizeof(FLOWBITS_OBJECT));
437
438 if (sfqueue_count(flowbits_bit_queue) > 0)
439 {
440 flowbits_item->id = (uint16_t)(uintptr_t)sfqueue_remove(flowbits_bit_queue);
441
442 }
443 else
444 {
445 flowbits_item->id = (uint16_t)flowbits_count;
446
447 flowbits_count++;
448
449 if(flowbits_count > giFlowbitSize)
450 {
451 ParseError("The number of flowbit IDs in the "
452 "current ruleset exceeds the maximum number of IDs "
453 "that are allowed (%d).", giFlowbitSize);
454 }
455 }
456
457 hstatus = sfghash_add(flowbits_hash, flowbitName, flowbits_item);
458 if(hstatus != SFGHASH_OK)
459 {
460 FatalError("Could not add flowbits key (%s) to hash.\n",flowbitName);
461 }
462 }
463
464 flowbits_item->toggle = flowbits_toggle;
465 flowbits_item->types |= flowbits->type;
466
467 switch (flowbits->type)
468 {
469 case FLOWBITS_SET:
470 case FLOWBITS_SETX:
471 case FLOWBITS_UNSET:
472 case FLOWBITS_TOGGLE:
473 case FLOWBITS_RESET:
474 flowbits_item->set++;
475 break;
476 case FLOWBITS_ISSET:
477 case FLOWBITS_ISNOTSET:
478 flowbits_item->isset++;
479 break;
480 default:
481 break;
482 }
483 udpateFlowBitGroupInfo(flowbits_grp, flowbitName, flowbits_item);
484
485 return flowbits_item;
486 }
487
getFlowBitGroup(char * groupName)488 static FLOWBITS_GRP *getFlowBitGroup(char *groupName)
489 {
490 int hstatus;
491 FLOWBITS_GRP *flowbits_grp = NULL;
492
493 if(!groupName)
494 return NULL;
495
496 if (!validateName(groupName))
497 {
498 ParseError("Flowbits: flowbits group name is limited to any alphanumeric string including %s",
499 ALLOWED_SPECIAL_CHARS);
500 }
501
502 flowbits_grp = (FLOWBITS_GRP *)sfghash_find(flowbits_grp_hash, groupName);
503
504 /*New group defined, add*/
505 if (flowbits_grp == NULL)
506 {
507 flowbits_grp = (FLOWBITS_GRP *)SnortAlloc(sizeof(FLOWBITS_GRP));
508 boInitBITOP(&(flowbits_grp->GrpBitOp), giFlowbitSizeInBytes);
509 boResetBITOP(&(flowbits_grp->GrpBitOp));
510 hstatus = sfghash_add(flowbits_grp_hash, groupName, flowbits_grp);
511 if(hstatus != SFGHASH_OK)
512 {
513 FatalError("Could not add flowbits group (%s) to hash.\n",groupName);
514 }
515 flowbits_grp_count++;
516 flowbits_grp->group_id = flowbits_grp_count;
517 flowbits_grp->name = SnortStrdup(groupName);
518
519 }
520
521 return flowbits_grp;
522 }
523
524 #ifdef DEBUG_MSGS
printOutFlowbits(FLOWBITS_OP * flowbits)525 static void printOutFlowbits(FLOWBITS_OP *flowbits)
526 {
527 int i;
528
529 DebugMessage(DEBUG_FLOWBITS,"flowbits: type = %d\n",flowbits->type);
530 DebugMessage(DEBUG_FLOWBITS,"flowbits: name = %s\n",flowbits->name);
531 DebugMessage(DEBUG_FLOWBITS,"flowbits: eval = %d\n",flowbits->eval);
532 DebugMessage(DEBUG_FLOWBITS,"flowbits: num_ids = %d\n",flowbits->num_ids);
533 DebugMessage(DEBUG_FLOWBITS,"flowbits: grp_id = %d\n",flowbits->group_id);
534 DebugMessage(DEBUG_FLOWBITS,"flowbits: group_name = %s\n",flowbits->group);
535 for (i = 0; i < flowbits->num_ids; i++)
536 {
537 DebugMessage(DEBUG_FLOWBITS,"flowbits: value = %d\n",flowbits->ids[i]);
538 }
539 }
540 #endif
541
542 /****************************************************************************
543 *
544 * Function: processFlowbits(char *, FlowBits *flowbits, OptTreeNode *)
545 *
546 * Purpose: parse the arguments to the flow plugin and alter the otn
547 * accordingly
548 *
549 * Arguments: flowbits => pointer to the current flowbits op
550 *
551 * Returns: void function
552 *
553 ****************************************************************************/
processFlowbits(char * flowbits_names,FLOWBITS_GRP * flowbits_grp,FLOWBITS_OP * flowbits)554 static void processFlowbits(char *flowbits_names, FLOWBITS_GRP *flowbits_grp, FLOWBITS_OP *flowbits)
555 {
556 char **toks;
557 int num_toks;
558 int i;
559 char *flowbits_name;
560
561 FLOWBITS_OBJECT *flowbits_item;
562
563 if (!flowbits_names || ((*flowbits_names) == 0))
564 {
565 return;
566 }
567
568 DEBUG_WRAP(DebugMessage(DEBUG_FLOWBITS, "flowbits tag id parsing %s\n",flowbits_names););
569
570 flowbits_name = SnortStrdup(flowbits_names);
571
572 if (NULL != strchr(flowbits_name, '|'))
573 {
574 if(NULL != strchr(flowbits_name, '&'))
575 {
576 ParseError("Flowbits: flowbits tag id operator '|' and '&' are used together.");
577 }
578 toks = mSplit(flowbits_name, "|", 0, &num_toks, 0);
579 flowbits->ids = SnortAlloc(num_toks*sizeof(*(flowbits->ids)));
580 flowbits->num_ids = num_toks;
581 for (i = 0; i < num_toks; i++)
582 {
583 flowbits_item = getFlowBitItem(toks[i], flowbits, flowbits_grp);
584 flowbits->ids[i] = flowbits_item->id;
585 }
586 flowbits->eval = FLOWBITS_OR;
587 mSplitFree(&toks, num_toks);
588 }
589 else if (NULL != strchr(flowbits_name, '&'))
590 {
591 toks = mSplit(flowbits_name, "&", 0, &num_toks, 0);
592 flowbits->ids = SnortAlloc(num_toks*sizeof(*(flowbits->ids)));
593 flowbits->num_ids = num_toks;
594 for (i = 0; i < num_toks; i++)
595 {
596 flowbits_item = getFlowBitItem(toks[i], flowbits, flowbits_grp);
597 flowbits->ids[i] = flowbits_item->id;
598 }
599 flowbits->eval = FLOWBITS_AND;
600 mSplitFree(&toks, num_toks);
601 }
602 else if (!strcasecmp(flowbits_name,"all"))
603 {
604 flowbits->eval = FLOWBITS_ALL;
605 }
606 else if (!strcasecmp(flowbits_name,"any"))
607 {
608 flowbits->eval = FLOWBITS_ANY;
609 }
610 else
611 {
612 flowbits_item = getFlowBitItem(flowbits_name, flowbits, flowbits_grp);
613 flowbits->ids = SnortAlloc(sizeof(*(flowbits->ids)));
614 flowbits->num_ids = 1;
615 flowbits->ids[0] = flowbits_item->id;
616 }
617
618 free(flowbits_name);
619
620 }
621
validateFlowbitsSyntax(FLOWBITS_OP * flowbits)622 void validateFlowbitsSyntax(FLOWBITS_OP *flowbits)
623 {
624 switch (flowbits->type)
625 {
626 case FLOWBITS_SET:
627 if ((flowbits->eval == FLOWBITS_AND) && (flowbits->ids))
628 break;
629
630 ParseError("Flowbits: operation set uses syntax: flowbits:set,bit[&bit],[group].");
631 break;
632 case FLOWBITS_SETX:
633 if ((flowbits->eval == FLOWBITS_AND)&&(flowbits->group) && (flowbits->ids) )
634 break;
635
636 ParseError("Flowbits: operation setx uses syntax: flowbits:setx,bit[&bit],group.");
637
638 break;
639 case FLOWBITS_UNSET:
640 if (((flowbits->eval == FLOWBITS_AND) && (!flowbits->group) && (flowbits->ids))
641 ||((flowbits->eval == FLOWBITS_ALL) && (flowbits->group)))
642 break;
643
644 ParseError("Flowbits: operation unset uses syntax: flowbits:unset,bit[&bit] OR"
645 " flowbits:unset, all, group.");
646
647 break;
648 case FLOWBITS_TOGGLE:
649 if (((flowbits->eval == FLOWBITS_AND) && (!flowbits->group) &&(flowbits->ids))
650 ||((flowbits->eval == FLOWBITS_ALL) && (flowbits->group)))
651 break;
652
653 ParseError("Flowbits: operation toggle uses syntax: flowbits:toggle,bit[&bit] OR"
654 " flowbits:toggle,all,group.");
655 break;
656 case FLOWBITS_ISSET:
657 if ((((flowbits->eval == FLOWBITS_AND) || (flowbits->eval == FLOWBITS_OR)) && (!flowbits->group) && flowbits->ids)
658 ||((((flowbits->eval == FLOWBITS_ANY))||(flowbits->eval == FLOWBITS_ALL)) && (flowbits->group)))
659 break;
660
661 ParseError("Flowbits: operation isset uses syntax: flowbits:isset,bit[&bit] OR "
662 "flowbits:isset,bit[|bit] OR flowbits:isset,all,group OR flowbits:isset,any,group.");
663
664 break;
665 case FLOWBITS_ISNOTSET:
666 if ((((flowbits->eval == FLOWBITS_AND) || (flowbits->eval == FLOWBITS_OR)) && (!flowbits->group) && flowbits->ids)
667 ||((((flowbits->eval == FLOWBITS_ANY))||(flowbits->eval == FLOWBITS_ALL)) && (flowbits->group)))
668 break;
669
670 ParseError("Flowbits: operation isnotset uses syntax: flowbits:isnotset,bit[&bit] OR "
671 "flowbits:isnotset,bit[|bit] OR flowbits:isnotset,all,group OR flowbits:isnotset,any,group.");
672
673 break;
674 case FLOWBITS_RESET:
675 if (flowbits->ids == NULL)
676 break;
677 ParseError("Flowbits: operation unset uses syntax: flowbits:reset OR flowbits:reset, group." );
678 break;
679
680 case FLOWBITS_NOALERT:
681 if ((flowbits->ids == NULL) && (flowbits->group == NULL))
682 break;
683 ParseError("Flowbits: operation noalert uses syntax: flowbits:noalert." );
684 break;
685
686 default:
687 ParseError("Flowbits: unknown operator.\n"
688 , file_name, file_line);
689 break;
690 }
691 }
692
processFlowBitsWithGroup(char * flowbitsName,char * groupName,FLOWBITS_OP * flowbits)693 void processFlowBitsWithGroup(char *flowbitsName, char *groupName, FLOWBITS_OP *flowbits)
694 {
695 FLOWBITS_GRP *flowbits_grp;
696
697 if (!flowbits_hash)
698 FlowBitsHashInit();
699
700 if ((groupName)&& (!flowbits_grp_hash))
701 FlowBitsGrpHashInit();
702
703 flowbits_grp = getFlowBitGroup(groupName);
704 processFlowbits(flowbitsName,flowbits_grp, flowbits);
705
706 if (groupName && !(flowbits->group))
707 {
708 flowbits->group = SnortStrdup(groupName);
709 flowbits->group_id = flowbits_grp->group_id;
710 }
711 validateFlowbitsSyntax(flowbits);
712 DEBUG_WRAP( printOutFlowbits(flowbits));
713
714 }
715 /****************************************************************************
716 *
717 * Function: FlowBitsParse(char *, FlowBits *flowbits, OptTreeNode *)
718 *
719 * Purpose: parse the arguments to the flow plugin and alter the otn
720 * accordingly
721 *
722 * Arguments: otn => pointer to the current rule option list node
723 *
724 * Returns: void function
725 *
726 ****************************************************************************/
FlowBitsParse(struct _SnortConfig * sc,char * data,FLOWBITS_OP * flowbits,OptTreeNode * otn)727 static void FlowBitsParse(struct _SnortConfig *sc, char *data, FLOWBITS_OP *flowbits, OptTreeNode *otn)
728 {
729 char **toks;
730 int num_toks;
731 char *typeName = NULL;
732 char *groupName = NULL;
733 char *flowbitsName = NULL;
734 FLOWBITS_GRP *flowbits_grp;
735
736 if (sc == NULL)
737 {
738 FatalError("%s(%d) Snort config for parsing is NULL.\n",
739 __FILE__, __LINE__);
740 }
741
742 DEBUG_WRAP(DebugMessage(DEBUG_FLOWBITS, "flowbits parsing %s\n",data););
743
744 toks = mSplit(data, ",", 0, &num_toks, 0);
745
746 if(num_toks < 1)
747 {
748 ParseError("ParseFlowArgs: Must specify flowbits operation.");
749 }
750 else if (num_toks > 3)
751 {
752 ParseError("ParseFlowArgs: Too many arguments.");
753 }
754
755 typeName = toks[0];
756
757 if(!strcasecmp("set",typeName))
758 {
759 flowbits->type = FLOWBITS_SET;
760 }
761 else if(!strcasecmp("setx",typeName))
762 {
763 flowbits->type = FLOWBITS_SETX;
764 }
765 else if(!strcasecmp("unset",typeName))
766 {
767 flowbits->type = FLOWBITS_UNSET;
768 }
769 else if(!strcasecmp("toggle",typeName))
770 {
771 flowbits->type = FLOWBITS_TOGGLE;
772 }
773 else if(!strcasecmp("isset",typeName))
774 {
775 flowbits->type = FLOWBITS_ISSET;
776 }
777 else if(!strcasecmp("isnotset",typeName))
778 {
779 flowbits->type = FLOWBITS_ISNOTSET;
780 }
781 else if(!strcasecmp("noalert", typeName))
782 {
783 if(num_toks > 1)
784 {
785 ParseError("Flowbits: Do not specify a flowbits tag id for the keyword 'noalert'.");
786 }
787
788 flowbits->type = FLOWBITS_NOALERT;
789 flowbits->ids = NULL;
790 flowbits->num_ids = 0;
791 flowbits->name = SnortStrdup(typeName);
792
793 mSplitFree(&toks, num_toks);
794 return;
795 }
796 else if(!strcasecmp("reset",typeName))
797 {
798
799 if(num_toks > 2)
800 {
801 ParseError("Flowbits: Too many arguments for the keyword 'reset'.");
802 }
803
804 if (num_toks == 2)
805 {
806 /*Save the group name*/
807 groupName = SnortStrdup(toks[1]);
808 flowbits_grp = getFlowBitGroup(groupName);
809 flowbits->group = groupName;
810 flowbits->group_id = flowbits_grp->group_id;
811 }
812 flowbits->type = FLOWBITS_RESET;
813 flowbits->ids = NULL;
814 flowbits->num_ids = 0;
815 flowbits->name = SnortStrdup(typeName);
816 mSplitFree(&toks, num_toks);
817 return;
818
819 }
820 else
821 {
822 ParseError("Flowbits: Invalid token %s.", typeName);
823 }
824
825 flowbits->name = SnortStrdup(typeName);
826 /*
827 ** Let's parse the flowbits name
828 */
829 if( num_toks < 2 )
830 {
831 ParseError("Flowbit: flowbits tag id must be provided.",
832 file_name, file_line);
833 }
834
835 flowbitsName = toks[1];
836
837 if(num_toks == 3)
838 {
839 groupName = toks[2];
840 }
841 processFlowBitsWithGroup(flowbitsName, groupName, flowbits);
842
843 mSplitFree(&toks, num_toks);
844 }
845
boUnSetGrpBit(BITOP * BitOp,char * group)846 static inline int boUnSetGrpBit(BITOP *BitOp, char *group)
847 {
848 FLOWBITS_GRP *flowbits_grp;
849 BITOP *GrpBitOp;
850 unsigned int i, max_bytes;
851
852 if( group == NULL )
853 return 0;
854 flowbits_grp = (FLOWBITS_GRP *)sfghash_find(flowbits_grp_hash, group);
855 if( flowbits_grp == NULL )
856 return 0;
857 if((BitOp == NULL) || (BitOp->uiMaxBits <= flowbits_grp->max_id) || flowbits_grp->count == 0)
858 return 0;
859 GrpBitOp = &(flowbits_grp->GrpBitOp);
860
861 /* note, max_id is an index, not a count.
862 * Calculate max_bytes by adding 8 to max_id, then dividing by 8. */
863 max_bytes = (flowbits_grp->max_id + 8) >> 3;
864 for ( i = 0; i < max_bytes; i++ )
865 {
866 BitOp->pucBitBuffer[i] &= ~GrpBitOp->pucBitBuffer[i];
867 }
868 return 1;
869 }
870
boToggleGrpBit(BITOP * BitOp,char * group)871 static inline int boToggleGrpBit(BITOP *BitOp, char *group)
872 {
873 FLOWBITS_GRP *flowbits_grp;
874 BITOP *GrpBitOp;
875 unsigned int i, max_bytes;
876
877 if( group == NULL )
878 return 0;
879 flowbits_grp = (FLOWBITS_GRP *)sfghash_find(flowbits_grp_hash, group);
880 if( flowbits_grp == NULL )
881 return 0;
882 if((BitOp == NULL) || (BitOp->uiMaxBits <= flowbits_grp->max_id) || flowbits_grp->count == 0)
883 return 0;
884 GrpBitOp = &(flowbits_grp->GrpBitOp);
885
886 /* note, max_id is an index, not a count.
887 * Calculate max_bytes by adding 8 to max_id, then dividing by 8. */
888 max_bytes = (flowbits_grp->max_id + 8) >> 3;
889 for ( i = 0; i < max_bytes; i++ )
890 {
891 BitOp->pucBitBuffer[i] ^= GrpBitOp->pucBitBuffer[i];
892 }
893 return 1;
894 }
895
boSetxBitsToGrp(BITOP * BitOp,uint16_t * ids,uint16_t num_ids,char * group)896 static inline int boSetxBitsToGrp(BITOP *BitOp, uint16_t *ids, uint16_t num_ids, char *group)
897 {
898 unsigned int i;
899 if (!boUnSetGrpBit(BitOp, group))
900 return 0;
901 for(i = 0; i < num_ids; i++)
902 boSetBit(BitOp,ids[i]);
903 return 1;
904 }
905
906
issetFlowbits(StreamFlowData * flowdata,uint8_t eval,uint16_t * ids,uint16_t num_ids,char * group)907 static inline int issetFlowbits(StreamFlowData *flowdata, uint8_t eval, uint16_t *ids,
908 uint16_t num_ids, char *group )
909 {
910 unsigned int i;
911 FLOWBITS_GRP *flowbits_grp;
912 Flowbits_eval evalType = (Flowbits_eval)eval;
913
914 switch (evalType)
915 {
916 case FLOWBITS_AND:
917 for(i = 0; i < num_ids; i++)
918 {
919 if(!boIsBitSet(&(flowdata->boFlowbits), ids[i]))
920 return 0;
921 }
922 return 1;
923 break;
924 case FLOWBITS_OR:
925 for(i = 0; i < num_ids; i++)
926 {
927 if(boIsBitSet(&(flowdata->boFlowbits), ids[i]))
928 return 1;
929 }
930 return 0;
931 break;
932 case FLOWBITS_ALL:
933 flowbits_grp = (FLOWBITS_GRP *)sfghash_find(flowbits_grp_hash, group);
934 if( flowbits_grp == NULL )
935 return 0;
936 for ( i = 0; i <= (unsigned int)(flowbits_grp->max_id >>3) ; i++ )
937 {
938 uint8_t val = flowdata->boFlowbits.pucBitBuffer[i] & flowbits_grp->GrpBitOp.pucBitBuffer[i];
939 if (val != flowbits_grp->GrpBitOp.pucBitBuffer[i])
940 return 0;
941 }
942 return 1;
943 break;
944 case FLOWBITS_ANY:
945 flowbits_grp = (FLOWBITS_GRP *)sfghash_find(flowbits_grp_hash, group);
946 if( flowbits_grp == NULL )
947 return 0;
948 for ( i = 0; i <= (unsigned int)(flowbits_grp->max_id >>3) ; i++ )
949 {
950 uint8_t val = flowdata->boFlowbits.pucBitBuffer[i] & flowbits_grp->GrpBitOp.pucBitBuffer[i];
951 if (val)
952 return 1;
953 }
954 return 0;
955 break;
956 default:
957 return 0;
958 }
959 }
960
961 /****************************************************************************
962 *
963 * Function: checkFlowBits
964 *
965 * Purpose: Check flow bits
966 *
967 * Arguments: Packet *p => packet data
968 * uint8_t type,
969 * uint8_t evalType,
970 * uint16_t *ids,
971 * uint16_t num_ids,
972 * char *group
973 *
974 * Returns: 0 on failure
975 *
976 ****************************************************************************/
checkFlowBits(uint8_t type,uint8_t evalType,uint16_t * ids,uint16_t num_ids,char * group,Packet * p)977 int checkFlowBits( uint8_t type, uint8_t evalType, uint16_t *ids, uint16_t num_ids, char *group, Packet *p)
978 {
979 int rval = DETECTION_OPTION_NO_MATCH;
980 StreamFlowData *flowdata;
981 Flowbits_eval eval = (Flowbits_eval) evalType;
982 int result = 0;
983 int i;
984
985 /* Need session pointer to get flowbits */
986 if ((stream_api == NULL) || (p->ssnptr == NULL))
987 return rval;
988
989
990 flowdata = session_api->get_flow_data(p);
991 if(!flowdata)
992 {
993 DEBUG_WRAP(DebugMessage(DEBUG_FLOWBITS, "No FLOWBITS_DATA"););
994 return rval;
995 }
996
997 switch(type)
998 {
999 case FLOWBITS_SET:
1000 for(i = 0; i < num_ids; i++)
1001 boSetBit(&(flowdata->boFlowbits),ids[i]);
1002 result = 1;
1003 break;
1004
1005 case FLOWBITS_SETX:
1006 result = boSetxBitsToGrp(&(flowdata->boFlowbits), ids, num_ids, group);
1007 break;
1008
1009 case FLOWBITS_UNSET:
1010 if (eval == FLOWBITS_ALL )
1011 boUnSetGrpBit(&(flowdata->boFlowbits), group);
1012 else
1013 {
1014 for(i = 0; i < num_ids; i++)
1015 boClearBit(&(flowdata->boFlowbits),ids[i]);
1016 }
1017 result = 1;
1018 break;
1019
1020 case FLOWBITS_RESET:
1021 if (!group)
1022 boResetBITOP(&(flowdata->boFlowbits));
1023 else
1024 boUnSetGrpBit(&(flowdata->boFlowbits), group);
1025 result = 1;
1026 break;
1027
1028 case FLOWBITS_ISSET:
1029
1030 if(issetFlowbits(flowdata,(uint8_t)eval, ids, num_ids, group))
1031 {
1032 result = 1;
1033 }
1034 else
1035 {
1036 rval = DETECTION_OPTION_FAILED_BIT;
1037 }
1038
1039 break;
1040
1041 case FLOWBITS_ISNOTSET:
1042 if(!issetFlowbits(flowdata, (uint8_t)eval, ids, num_ids, group))
1043 {
1044 result = 1;
1045 }
1046 else
1047 {
1048 rval = DETECTION_OPTION_FAILED_BIT;
1049 }
1050 break;
1051
1052 case FLOWBITS_TOGGLE:
1053 if (group)
1054 boToggleGrpBit(&(flowdata->boFlowbits),group);
1055 else
1056 {
1057 for(i = 0; i < num_ids; i++)
1058 {
1059 if (boIsBitSet(&(flowdata->boFlowbits),ids[i]))
1060 {
1061 boClearBit(&(flowdata->boFlowbits),ids[i]);
1062 }
1063 else
1064 {
1065 boSetBit(&(flowdata->boFlowbits),ids[i]);
1066 }
1067 }
1068 }
1069 result = 1;
1070
1071 break;
1072
1073 case FLOWBITS_NOALERT:
1074 /*
1075 ** This logic allows us to put flowbits: noalert any where
1076 ** in the detection chain, and still do bit ops after this
1077 ** option.
1078 */
1079 return DETECTION_OPTION_NO_ALERT;
1080
1081 default:
1082 /*
1083 ** Always return failure here.
1084 */
1085 return rval;
1086 }
1087
1088 /*
1089 ** Now return what we found
1090 */
1091 if (result == 1)
1092 {
1093 rval = DETECTION_OPTION_MATCH;
1094 }
1095
1096 return rval;
1097 }
1098
1099 /****************************************************************************
1100 *
1101 * Function: FlowBitsCheck(Packet *, struct _OptTreeNode *, OptFpList *)
1102 *
1103 * Purpose: Check flow bits foo
1104 *
1105 * Arguments: data => argument data
1106 * otn => pointer to the current rule's OTN
1107 *
1108 * Returns: 0 on failure
1109 *
1110 ****************************************************************************/
FlowBitsCheck(void * option_data,Packet * p)1111 int FlowBitsCheck(void *option_data, Packet *p)
1112 {
1113 FLOWBITS_OP *flowbits = (FLOWBITS_OP*)option_data;
1114 int rval = DETECTION_OPTION_NO_MATCH;
1115
1116 PROFILE_VARS;
1117
1118 if (!flowbits)
1119 return rval;
1120
1121 PREPROC_PROFILE_START(flowBitsPerfStats);
1122
1123 rval = checkFlowBits( flowbits->type, (uint8_t)flowbits->eval,
1124 flowbits->ids, flowbits->num_ids, flowbits->group, p);
1125
1126 PREPROC_PROFILE_END(flowBitsPerfStats);
1127 return rval;
1128 }
1129
1130 /****************************************************************************
1131 *
1132 * Function: FlowBitsVerify()
1133 *
1134 * Purpose: Check flow bits foo to make sure its valid
1135 *
1136 * Arguments:
1137 *
1138 * Returns: 0 on failure
1139 *
1140 ****************************************************************************/
FlowBitsVerify(void)1141 void FlowBitsVerify(void)
1142 {
1143 SFGHASH_NODE * n;
1144 FLOWBITS_OBJECT *fb;
1145 int num_flowbits = 0;
1146
1147 if (flowbits_hash == NULL)
1148 return;
1149
1150 for (n = sfghash_findfirst(flowbits_hash);
1151 n != NULL;
1152 n= sfghash_findnext(flowbits_hash))
1153 {
1154 fb = (FLOWBITS_OBJECT *)n->data;
1155
1156 if (fb->toggle != flowbits_toggle)
1157 {
1158 if (sfqueue_add(flowbits_bit_queue, (NODE_DATA)(uintptr_t)fb->id) == -1)
1159 {
1160 FatalError("%s(%d) Failed to add flow bit id to queue.\n",
1161 __FILE__, __LINE__);
1162 }
1163
1164 sfghash_remove(flowbits_hash, n->key);
1165 continue;
1166 }
1167
1168 if ((fb->set > 0) && (fb->isset == 0))
1169 {
1170 LogMessage("WARNING: flowbits key '%s' is set but not ever checked.\n",
1171 (char*)n->key);
1172 }
1173 else if ((fb->isset > 0) && (fb->set == 0))
1174 {
1175 LogMessage("WARNING: flowbits key '%s' is checked but not ever set.\n",
1176 (char*)n->key);
1177 }
1178 else if ((fb->set == 0) && (fb->isset == 0))
1179 {
1180 continue; /* don't count this bit as used */
1181 }
1182
1183 num_flowbits++;
1184 }
1185
1186 flowbits_toggle ^= 1;
1187
1188 LogMessage("%d out of %d flowbits in use.\n",
1189 num_flowbits, giFlowbitSize);
1190 }
1191
FlowBitsCleanExit(int signal,void * data)1192 static void FlowBitsCleanExit(int signal, void *data)
1193 {
1194 #ifdef SNORT_RELOAD
1195 if (reloadInProgress)
1196 {
1197 return;
1198 }
1199 #endif
1200
1201 if (flowbits_hash != NULL)
1202 {
1203 sfghash_delete(flowbits_hash);
1204 flowbits_hash = NULL;
1205 }
1206
1207 if (flowbits_grp_hash != NULL)
1208 {
1209 sfghash_delete(flowbits_grp_hash);
1210 flowbits_grp_hash = NULL;
1211 }
1212
1213 if (flowbits_bit_queue != NULL)
1214 {
1215 sfqueue_free_all(flowbits_bit_queue, NULL);
1216 flowbits_bit_queue = NULL;
1217 }
1218 }
setFlowbitSize(char * args)1219 void setFlowbitSize(char *args)
1220 {
1221 char *endptr;
1222 long int size;
1223 size = SnortStrtol(args, &endptr, 0);
1224 if ((errno == ERANGE) || (*endptr != '\0') ||
1225 (size < 0) || (size > MAX_FLOWBIT_SIZE))
1226 {
1227 ParseError("Invalid argument to 'flowbits_size': %s. Must be a "
1228 "positive integer and less than %d.", args, MAX_FLOWBIT_SIZE);
1229 }
1230
1231 giFlowbitSize = size;
1232 giFlowbitSizeInBytes = CONVERT_BITS_TO_BYTES(giFlowbitSize);
1233 }
1234
getFlowbitSize(void)1235 unsigned int getFlowbitSize(void)
1236 {
1237 return giFlowbitSize;
1238 }
getFlowbitSizeInBytes(void)1239 unsigned int getFlowbitSizeInBytes(void)
1240 {
1241 return giFlowbitSizeInBytes;
1242 }
1243
FlowbitResetCounts(void)1244 void FlowbitResetCounts(void)
1245 {
1246 SFGHASH_NODE *n;
1247 FLOWBITS_OBJECT *fb;
1248
1249 if (flowbits_hash == NULL)
1250 return;
1251
1252 for (n = sfghash_findfirst(flowbits_hash);
1253 n != NULL;
1254 n = sfghash_findnext(flowbits_hash))
1255 {
1256 fb = (FLOWBITS_OBJECT *)n->data;
1257 fb->set = 0;
1258 fb->isset = 0;
1259 }
1260 }
1261