Main Page | Modules | Class List | Directories | File List | Class Members | File Members | Related Pages

sp_flowbits.c

Go to the documentation of this file.
00001 /*
00002 ** $Id$
00003 **
00004 ** sp_flowbits
00005 ** 
00006 ** Purpose:
00007 **
00008 ** Wouldn't it be nice if we could do some simple state tracking 
00009 ** across multiple packets?  Well, this allows you to do just that.
00010 **
00011 ** Effect:
00012 **
00013 ** - [Un]set a bitmask stored with the session
00014 ** - Check the value of the bitmask
00015 **
00016 ** Copyright (C) 2003 Sourcefire, Inc
00017 ** 
00018 ** This program is free software; you can redistribute it and/or modify
00019 ** it under the terms of the GNU General Public License as published by
00020 ** the Free Software Foundation; either version 2 of the License, or
00021 ** (at your option) any later version.
00022 **
00023 ** This program is distributed in the hope that it will be useful,
00024 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00025 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00026 ** GNU General Public License for more details.
00027 **
00028 ** You should have received a copy of the GNU General Public License
00029 ** along with this program; if not, write to the Free Software
00030 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00031 */
00032 
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <ctype.h>
00036 #include <sys/types.h>
00037 
00038 #include "rules.h"
00039 #include "decode.h"
00040 #include "plugbase.h"
00041 #include "parser.h"
00042 #include "debug.h"
00043 #include "util.h"
00044 #include "plugin_enum.h"
00045 #include "snort.h"
00046 #include "flow.h"
00047 #include "bitop.h"
00048 #include "sfghash.h"
00049 #include "flow/flow.h"
00050 #include "spp_flow.h"
00051 
00052 /**
00053 **  The FLOWBITS_OBJECT is used to track the different
00054 **  flowbit names that set/unset/etc. bits.  We use these
00055 **  so that we can verify that the rules that use flowbits
00056 **  make sense.
00057 **
00058 **  The types element tracks all the different operations that
00059 **  may occur for a given object.  This is different from how
00060 **  the type element is used from the FLOWBITS_ITEM structure.
00061 */
00062 typedef struct _FLOWBITS_OBJECT
00063 {
00064     u_int32_t id;
00065     u_int8_t  types;
00066 } FLOWBITS_OBJECT;
00067 
00068 /**
00069 **  This structure is the context ptr for each detection option
00070 **  on a rule.  The id is associated with a FLOWBITS_OBJECT id.
00071 **
00072 **  The type element track only one operation.
00073 */
00074 typedef struct _FLOWBITS_OP
00075 {
00076     u_int32_t id;
00077     u_int8_t  type;        /* Set, Unset, Invert, IsSet, IsNotSet, Reset  */
00078 } FLOWBITS_OP;
00079 
00080 #define FLOWBITS_SET       0x01  
00081 #define FLOWBITS_UNSET     0x02
00082 #define FLOWBITS_TOGGLE    0x04
00083 #define FLOWBITS_ISSET     0x08
00084 #define FLOWBITS_ISNOTSET  0x10
00085 #define FLOWBITS_RESET     0x20
00086 #define FLOWBITS_NOALERT   0x40
00087 
00088 extern unsigned int giFlowbitSize;
00089 
00090 static u_int32_t flowbits_count = 0;
00091 static SFGHASH *flowbits_hash;
00092 
00093 static void FlowBitsInit(char *, OptTreeNode *, int);
00094 static void FlowBitsParse(char *, FLOWBITS_OP *, OptTreeNode *);
00095 static int  FlowBitsCheck(Packet *, struct _OptTreeNode *, OptFpList *);
00096 
00097 /****************************************************************************
00098  * 
00099  * Function: SetupFlowBits()
00100  *
00101  * Purpose: Generic detection engine plugin template.  Registers the
00102  *          configuration function and links it to a rule keyword.  This is
00103  *          the function that gets called from InitPlugins in plugbase.c.
00104  *
00105  * Arguments: None.
00106  *
00107  * Returns: void function
00108  *
00109  * 3/4/05 - man beefed up the hash table size from 100 -> 10000
00110  *
00111  ****************************************************************************/
00112 void SetupFlowBits()
00113 {
00114     /* setup our storage hash */
00115     flowbits_hash = sfghash_new( 10000, 0 , 0, 0);
00116     if (!flowbits_hash) {
00117         FatalError("Could not setup flowbits hash\n");
00118     }
00119 
00120     /* map the keyword to an initialization/processing function */
00121     RegisterPlugin("flowbits", FlowBitsInit);
00122 
00123     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Plugin: FlowBits Setup\n"););
00124 }
00125 
00126 
00127 /****************************************************************************
00128  * 
00129  * Function: FlowBitsInit(char *, OptTreeNode *)
00130  *
00131  * Purpose: Configure the flow init option to register the appropriate checks
00132  *
00133  * Arguments: data => rule arguments/data
00134  *            otn => pointer to the current rule option list node
00135  *
00136  * Returns: void function
00137  *
00138  ****************************************************************************/
00139 static void FlowBitsInit(char *data, OptTreeNode *otn, int protocol)
00140 {
00141     FLOWBITS_OP *flowbits;
00142     OptFpList *fpl;
00143  
00144     if(!SppFlowIsRunning())
00145     {
00146         LogMessage("Warning: %s (%d) => flowbits without flow.  flow must be enabled for this plugin.\n", file_name,file_line);
00147     }
00148 
00149     flowbits = (FLOWBITS_OP *) SnortAlloc(sizeof(FLOWBITS_OP));
00150     if (!flowbits) {
00151         FatalError("%s (%d): Unable to allocate flowbits node\n", file_name,
00152                 file_line);
00153     }
00154 
00155     FlowBitsParse(data, flowbits, otn);
00156     fpl = AddOptFuncToList(FlowBitsCheck, otn);
00157 
00158     /*
00159      * attach it to the context node so that we can call each instance 
00160      * individually
00161      */
00162     
00163     fpl->context = (void *) flowbits;
00164     return;
00165 }
00166 
00167 
00168 /****************************************************************************
00169  * 
00170  * Function: FlowBitsParse(char *, FlowBits *flowbits, OptTreeNode *)
00171  *
00172  * Purpose: parse the arguments to the flow plugin and alter the otn
00173  *          accordingly
00174  *
00175  * Arguments: otn => pointer to the current rule option list node
00176  *
00177  * Returns: void function
00178  *
00179  ****************************************************************************/
00180 static void FlowBitsParse(char *data, FLOWBITS_OP *flowbits, OptTreeNode *otn)
00181 {
00182     FLOWBITS_OBJECT *flowbits_item;
00183     char *token, *str, *p;
00184     u_int32_t id = 0;
00185     int hstatus;
00186 
00187     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "flowbits parsing %s\n",data););
00188     
00189     str = strdup(data);
00190 
00191     if(str == NULL)
00192     {
00193         FatalError("ParseFlowArgs: Can't strdup data\n");
00194     }
00195 
00196     p = str;
00197 
00198     /* nuke leading whitespace */
00199     while(isspace((int)*p)) p++;
00200 
00201     token = strtok(p, ",");
00202     if(!token)
00203     {
00204         FatalError("%s(%d) ParseFlowArgs: Must specify flowbits operation.",
00205                 file_name, file_line);
00206     }
00207 
00208     while(isspace((int)*token))
00209         token++;
00210 
00211     if(!strncasecmp("set",token,3))
00212     {
00213         flowbits->type = FLOWBITS_SET;
00214     } 
00215     else if(!strncasecmp("unset",token,5))
00216     {
00217         flowbits->type = FLOWBITS_UNSET;
00218     }
00219     else if(!strncasecmp("toggle",token,6))
00220     {
00221         flowbits->type = FLOWBITS_TOGGLE;
00222     }
00223     else if(!strncasecmp("isset",token,5))
00224     {
00225         flowbits->type = FLOWBITS_ISSET;
00226     }
00227     else if(!strncasecmp("isnotset",token,8))
00228     {
00229         flowbits->type = FLOWBITS_ISNOTSET;
00230     } 
00231     else if(!strncasecmp("noalert", token, 7))
00232     {
00233         if(strtok(NULL, " ,\t"))
00234         {
00235             FatalError("%s (%d): Do not specify a flowbits tag id for the "
00236                        "keyword 'noalert'.\n", file_name, file_line);
00237         }
00238 
00239         flowbits->type = FLOWBITS_NOALERT;
00240         flowbits->id   = 0;
00241         free(str);
00242         return;
00243     }
00244     else if(!strncasecmp("reset",token,5))
00245     {
00246         if(strtok(NULL, " ,\t"))
00247         {
00248             FatalError("%s (%d): Do not specify a flowbits tag id for the "
00249                        "keyword 'reset'.\n", file_name, file_line);
00250         }
00251 
00252         flowbits->type = FLOWBITS_RESET;
00253         flowbits->id   = 0;
00254         free(str);
00255         return;
00256     } 
00257     else
00258     {
00259         FatalError("%s(%d) ParseFlowArgs: Invalid token %s\n",
00260                 file_name, file_line, token);
00261     }
00262 
00263     /*
00264     **  Let's parse the flowbits name
00265     */
00266     token = strtok(NULL, " ,\t");
00267     if(!token || !strlen(token))
00268     {
00269         FatalError("%s (%d): flowbits tag id must be provided\n",
00270                 file_name, file_line);
00271     }
00272 
00273     /*
00274     **  Take space from the beginning
00275     */
00276     while(isspace((int)*token)) 
00277         token++;
00278 
00279     /*
00280     **  Do we still have a ID tag left.
00281     */
00282     if (!strlen(token))
00283     {
00284         FatalError("%s (%d): flowbits tag id must be provided\n",
00285                 file_name, file_line);
00286     }
00287 
00288     /*
00289     **  Is there a anything left?
00290     */
00291     if(strtok(NULL, " ,\t"))
00292     {
00293         FatalError("%s (%d): flowbits tag id cannot include spaces or "
00294                    "commas.\n", file_name, file_line);
00295     }
00296     
00297     flowbits_item = (FLOWBITS_OBJECT *)sfghash_find(flowbits_hash, token);
00298 
00299     if (flowbits_item) 
00300     {
00301         flowbits_item->types |= flowbits->type;
00302         id = flowbits_item->id;
00303     } 
00304     else
00305     {
00306         flowbits_item = 
00307             (FLOWBITS_OBJECT *)SnortAlloc(sizeof(FLOWBITS_OBJECT));
00308 
00309         flowbits_item->id = flowbits_count;
00310         flowbits_item->types |= flowbits->type;
00311 
00312         hstatus = sfghash_add( flowbits_hash, token, flowbits_item);
00313         if(hstatus) 
00314         {
00315             FatalError("Could not add flowbits key (%s) to hash",token);
00316         }
00317 
00318         id = flowbits_count;
00319 
00320         flowbits_count++;
00321 
00322         if(flowbits_count >= (giFlowbitSize<<3) )
00323         {
00324             FatalError("FLOWBITS ERROR: The number of flowbit IDs in the "
00325                        "current ruleset (%d) exceed the maximum number of IDs "
00326                        "that are allowed (%d).\n", flowbits_count,giFlowbitSize<<3);
00327         }
00328     }
00329 
00330     flowbits->id = id;
00331 
00332     free(str);
00333 }
00334 
00335 static int ResetFlowbits(Packet *p)
00336 {
00337     Session *ssn;
00338 
00339     if(!p || !p->ssnptr)
00340     {
00341         return 0;
00342     }
00343 
00344     ssn = p->ssnptr;
00345 
00346     /*
00347     **  Check session_flags for new TCP session
00348     **
00349     **  PKT_STREAM_EST is pretty obvious why it's in here
00350     **
00351     **  SEEN_CLIENT and SEEN_SERVER allow us to only reset the bits
00352     **  once on the first SYN pkt.  There after bits will be
00353     **  accumulated for that session.
00354     */
00355     if((p->packet_flags & PKT_STREAM_EST) ||
00356        ((ssn->session_flags & (SSNFLAG_SEEN_CLIENT | SSNFLAG_SEEN_SERVER)) == 
00357         (SSNFLAG_SEEN_CLIENT | SSNFLAG_SEEN_SERVER)))
00358     {
00359         return 0;
00360     }
00361 
00362     return 1;
00363 }
00364 
00365 /*
00366 **  NAME
00367 **    GetFlowbitsData::
00368 */
00369 /**
00370 **  This function initializes/retrieves flowbits data that is associated
00371 **  with a given flow.
00372 */
00373 static FLOWDATA *GetFlowbitsData(Packet *p)
00374 {
00375     FLOW     *fp;
00376     FLOWDATA *flowdata;
00377 
00378     if(!p->flow)
00379     {
00380         return NULL;
00381     }
00382 
00383     fp = (FLOW *)p->flow;
00384 
00385     flowdata = &(fp->data);
00386     if(!flowdata)
00387         return NULL;
00388 
00389     /*
00390     **  Since we didn't initialize BITOP (which resets during init)
00391     **  we have to check for resetting here, because it may be
00392     **  a new flow.
00393     **
00394     **  NOTE:
00395     **    We can only do this on TCP flows because we know when a
00396     **    connection begins and ends.  So that's what we check.
00397     */
00398     if(ResetFlowbits(p))
00399     {
00400         boResetBITOP(&(flowdata->boFlowbits));
00401     }
00402 
00403     return flowdata;
00404 }
00405 
00406 /****************************************************************************
00407  * 
00408  * Function: FlowBitsCheck(Packet *, struct _OptTreeNode *, OptFpList *)
00409  *
00410  * Purpose: Check flow bits foo 
00411  *
00412  * Arguments: data => argument data
00413  *            otn => pointer to the current rule's OTN
00414  *
00415  * Returns: 0 on failure
00416  *
00417  ****************************************************************************/
00418 static int FlowBitsCheck(Packet *p,struct _OptTreeNode *otn, OptFpList *fp_list)
00419 {
00420     FLOWBITS_OP *flowbits;   /* pointer to the eval struct */
00421     FLOWDATA *flowdata;
00422     int result = 0;
00423 
00424     if(!p)
00425     {
00426         DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, 
00427                    "FLOWBITSCHECK: No pkt."););
00428         return 0;
00429     }
00430 
00431     flowdata = GetFlowbitsData(p);
00432     if(!flowdata)
00433     {
00434         DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "No FLOWBITS_DATA"););
00435         return 0;
00436     }
00437 
00438     flowbits = (FLOWBITS_OP *) fp_list->context;
00439 
00440     DEBUG_WRAP
00441     (
00442         DebugMessage(DEBUG_PLUGIN,"flowbits: type = %d\n",flowbits->type);
00443         DebugMessage(DEBUG_PLUGIN,"flowbits: value = %d\n",flowbits->id);
00444     );
00445 
00446     switch(flowbits->type)
00447     {
00448         case FLOWBITS_SET:
00449             boSetBit(&(flowdata->boFlowbits),flowbits->id);
00450             result = 1;
00451             break;
00452 
00453         case FLOWBITS_UNSET:
00454             boClearBit(&(flowdata->boFlowbits),flowbits->id);
00455             result = 1;
00456             break;
00457 
00458         case FLOWBITS_RESET:
00459             boResetBITOP(&(flowdata->boFlowbits));
00460             result = 1;
00461             break;
00462 
00463         case FLOWBITS_ISSET:
00464             if(boIsBitSet(&(flowdata->boFlowbits),flowbits->id))
00465             {
00466                 result = 1;
00467             }
00468             break;
00469 
00470         case FLOWBITS_ISNOTSET:
00471             if (boIsBitSet(&(flowdata->boFlowbits),flowbits->id))
00472             {
00473                 result = 0;
00474             }
00475             else
00476             {
00477                 result = 1;
00478             }
00479             break;
00480 
00481         case FLOWBITS_TOGGLE:
00482             if (boIsBitSet(&(flowdata->boFlowbits),flowbits->id))
00483             {
00484                 boClearBit(&(flowdata->boFlowbits),flowbits->id);
00485             }
00486             else
00487             {
00488                 boSetBit(&(flowdata->boFlowbits),flowbits->id);
00489             }
00490 
00491             result = 1;
00492 
00493             break;
00494 
00495         case FLOWBITS_NOALERT:
00496             /*
00497             **  This logic allows us to put flowbits: noalert any where
00498             **  in the detection chain, and still do bit ops after this
00499             **  option.
00500             */
00501             fp_list->next->OptTestFunc(p, otn, fp_list->next);
00502             return 0;
00503 
00504         default:
00505             /*
00506             **  Always return failure here.
00507             */
00508             return 0;
00509     }
00510     
00511     /*
00512     **  Now return what we found
00513     */
00514     if (result == 1)
00515     {
00516         return fp_list->next->OptTestFunc(p, otn, fp_list->next);
00517     }
00518 
00519     return 0;
00520 }
00521 
00522 /****************************************************************************
00523  * 
00524  * Function: FlowBitsVerify()
00525  *
00526  * Purpose: Check flow bits foo to make sure its valid
00527  *
00528  * Arguments: 
00529  *
00530  * Returns: 0 on failure
00531  *
00532  ****************************************************************************/
00533 void FlowBitsVerify(void)
00534 {
00535     SFGHASH_NODE * n;
00536     FLOWBITS_OBJECT *fb;
00537 
00538     for (n = sfghash_findfirst(flowbits_hash); 
00539          n != 0; 
00540          n= sfghash_findnext(flowbits_hash))
00541     {
00542         fb = (FLOWBITS_OBJECT *)n->data;
00543 
00544         if (fb->types & FLOWBITS_SET) {
00545             if (!(fb->types & FLOWBITS_ISSET || fb->types & FLOWBITS_ISNOTSET))
00546             {
00547                 LogMessage("Warning: flowbits key '%s' is set but not ever checked.\n",n->key);
00548             }
00549         } else {
00550             if (fb->types & FLOWBITS_ISSET || fb->types & FLOWBITS_ISNOTSET) {
00551                 LogMessage("Warning: flowbits key '%s' is checked but not ever set.\n",n->key);
00552             }
00553         }
00554     }
00555 }

Generated on Sun May 14 14:51:13 2006 by  doxygen 1.4.2