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

spp_clamav.c

Go to the documentation of this file.
00001 /* $Id: ClamAV-2.3.3-1.diff,v 1.1.1.1 2005/05/06 21:19:36 jonkman Exp $ */
00002 /* Snort Preprocessor for Antivirus Checking with ClamAV */
00003 
00004 /*
00005 ** Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
00006 ** Copyright (C) 2003 Sourcefire, Inc.
00007 ** Copyright (C) 2004 William Metcalf <William_Metcalf@kcmo.org> and
00008 **                    Victor Julien <victor@nk.nl>
00009 **
00010 ** This program is free software; you can redistribute it and/or modify
00011 ** it under the terms of the GNU General Public License as published by
00012 ** the Free Software Foundation; either version 2 of the License, or
00013 ** (at your option) any later version.
00014 **
00015 ** This program is distributed in the hope that it will be useful,
00016 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 ** GNU General Public License for more details.
00019 **
00020 ** You should have received a copy of the GNU General Public License
00021 ** along with this program; if not, write to the Free Software
00022 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00023 */
00024 
00025 #ifdef CLAMAV
00026 
00027 /* spp_clamav.c
00028  *
00029  * Purpose: Sends packet p to ClamAV for Antivirus checking.
00030  *
00031  * Arguments: None
00032  *
00033  * Effect: Who needs virus.rules??? :-)
00034  *
00035  * Comments:
00036  *
00037  *
00038  * TODO:
00039  * - documentation
00040  * - are the defaultports in ParseClamAVArgs ok?
00041  * - options structure like s4data in Stream4 for cl_root, VirusScanPorts, drop/reject/alert, defs dirlocation **IN PROGRESS**
00042  * - maybe more protocol specific support for less false negatives?
00043  *
00044  *
00045  * Changes:
00046  *
00047  * 2004/11/10: added code for the automatic reloading of the virusdefs
00048  *             added support for ClamAV 0.80
00049  * 2006/02/03: added check for http traffic so we better handle http downloads
00050  *             removed cl_scanbuf since it was broken anyway
00051  *             cleanups and comments added.
00052  *             added 8080 to the default ports
00053  *
00054  */
00055 #ifdef HAVE_CONFIG_H
00056 #include "config.h"
00057 #endif
00058 
00059 #ifndef DEBUG
00060    #ifndef INLINE
00061        #define INLINE inline
00062    #endif
00063 #else
00064    #ifdef INLINE
00065        #undef INLINE
00066    #endif
00067    #define INLINE
00068 #endif /* DEBUG */
00069 
00070 #include <sys/types.h>
00071 #include <stdlib.h>
00072 #include <ctype.h>
00073 #include <rpc/types.h>
00074 #include <errno.h>
00075 #include "generators.h"
00076 #include "event_wrapper.h"
00077 #include "util.h"
00078 #include "plugbase.h"
00079 #include "parser.h"
00080 #include "decode.h"
00081 #include "debug.h"
00082 #include "mstring.h"
00083 #include "log.h"
00084 #include "spp_clamav.h"
00085 #include "stream.h"
00086 #include "preprocessors/spp_stream4.h"
00087 #ifdef GIDS
00088 #include "preprocessors/spp_stickydrop.h"
00089 #include "inline.h"
00090 #endif
00091 
00092 #include "snort.h"
00093 #include <clamav.h>
00094 
00095 #ifdef HAVE_STRINGS_H
00096 #include <strings.h>
00097 #endif
00098 
00099 #ifdef GIDS
00100 /* stickydrop */
00101 extern SDtimeout sdt;
00102 extern Stream4Data s4data;
00103 #endif /* GIDS */
00104 
00105 /* we need this to stringify the CLAMAV_DEFDIR which is supplied at compiletime see:
00106   http://gcc.gnu.org/onlinedocs/gcc-3.4.1/cpp/Stringification.html#Stringification */
00107 #define xstr(s) str(s)
00108 #define str(s) #s
00109 
00110 /* the config struct */
00111 struct ClamAVConfig
00112 {
00113    /* scan limitations */
00114    char toclientonly; /* if set to 1 scan only traffic to the client */
00115    char toserveronly; /* if set to 1 scan only traffic to the server */
00116    char VirusScanPorts[65536/8]; /* array containing info about which ports we care about */
00117 
00118    /* actions */
00119    char drop;
00120    char reset;
00121    char rboth;
00122 
00123    /* virdef dir */
00124    char dbdir[255];
00125 
00126    /* temp dir for file descriptors */
00127    char desctmpdir[255];
00128 
00129    /* reload time in seconds */
00130    u_int16_t reloadtime;
00131    u_int32_t next_reload_time;
00132 
00133 } clamcnf;
00134 
00135 /* pointer to ClamAV's in-memory virusdatabase */
00136 struct cl_node *cl_root;
00137 /* scanner limits */
00138 struct cl_limits clam_limits;
00139 static void ClamAVInit(u_char *);
00140 extern void SetupClamAV();
00141 static int VirusInPacket(Packet *);
00142 int check_4_http_headers(u_int8_t *, int);
00143 static void VirusChecker(Packet *, void *);
00144 extern u_int32_t event_id;
00145 
00146 /* db reloading */
00147 struct cl_stat dbstat;
00148 
00149 
00150 /*
00151  * Function: SetupClamAV()
00152  *
00153  * Purpose: Registers the preprocessor.
00154  *
00155  * Arguments: None.
00156  *
00157  * Returns: void function
00158  *
00159  */
00160 void SetupClamAV()
00161 {
00162    RegisterPreprocessor("ClamAV", ClamAVInit);
00163 }
00164 
00165 
00166 /*
00167  * Function: ProcessPorts(u_char *)
00168  *
00169  * Purpose: Sets the port limits
00170  *
00171  * Arguments: pointer to string with portlist.
00172  *
00173  * Returns: void function
00174  *
00175  */
00176 static void ProcessPorts(u_char *portlist)
00177 {
00178    int j = 0;
00179    int i = 0;
00180    char **ports;
00181    int num_ports;
00182    char *port;
00183    u_int32_t portnum;
00184 
00185    /* reset the ports array */
00186    bzero(&clamcnf.VirusScanPorts, sizeof(clamcnf.VirusScanPorts));
00187 
00188    ports = mSplit(portlist, " ", 40, &num_ports, 0);
00189 
00190    /* run through the ports */
00191    for(j = 0; j < num_ports; j++)
00192    {
00193        port = ports[j];
00194 
00195        /* we need to set this port */
00196        if(isdigit((int)port[0]))
00197        {
00198            portnum = atoi(port);
00199            if(portnum > 65535)
00200            {
00201                FatalError("%s(%d) => Bad port list to scan: "
00202                    "port '%d' out of range\n", portnum, file_name, file_line);
00203            }
00204 
00205            /* mark this port as being interesting using some portscan2-type voodoo,
00206               and also add it to the port list string while we're at it so we can
00207               later print out all the ports with a single LogMessage() */
00208            clamcnf.VirusScanPorts[(portnum/8)] |= 1<<(portnum%8);
00209        }
00210        /* we need to unset this port */
00211        else if(port[0] == '!')
00212        {
00213            for(i = 0; i < strlen(port) && port[i+1] != '\0'; i++)
00214            {
00215                port[i] = port[i+1];
00216            }
00217            port[i] = '\0';
00218 
00219            if(isdigit((int)port[0]))
00220            {
00221                portnum = atoi(port);
00222                if(portnum > 65535)
00223                {
00224                    FatalError("%s(%d) => Bad port list to scan: "
00225                        "port '%d' out of range\n", portnum, file_name, file_line);
00226                }
00227 
00228                /* clear the bit - this removes the port from the array */
00229                clamcnf.VirusScanPorts[(portnum/8)] &= ~(1<<(portnum%8));
00230            }
00231            else
00232            {
00233                FatalError("%s(%d) => Bad port list to scan: "
00234                           "bad port\n", file_name, file_line);
00235            }
00236        }
00237        /* we need to set all ports */
00238        else if(!strncasecmp(port, "all", 3))
00239        {
00240            /* enable all ports */
00241            for(portnum = 0; portnum <= 65535; portnum++)
00242                clamcnf.VirusScanPorts[(portnum/8)] |= 1<<(portnum%8);
00243        }
00244        else if(!strncasecmp(port, "ports", 5));
00245        else
00246        {
00247            FatalError("%s(%d) => Bad port list to scan: "
00248                       "bad port\n", file_name, file_line);
00249        }
00250    }
00251 
00252    mSplitFree(&ports, num_ports);
00253 
00254    /* some pretty printing */
00255    if(!pv.quiet_flag)
00256    {
00257        /* print the portlist */
00258        LogMessage("    Ports: ");
00259 
00260        for(portnum = 0, j = 0; portnum <= 65535; portnum++)
00261        {
00262            if((clamcnf.VirusScanPorts[(portnum/8)] & (1<<(portnum%8))))
00263            {
00264                LogMessage("%d ", portnum);
00265                j++;
00266            }
00267 
00268            if(j > 20)
00269            {
00270                LogMessage("...\n");
00271                return;
00272            }
00273        }
00274    }
00275 }
00276 
00277 
00278  /*
00279  * Function: ParseClamAVArgs(u_char *)
00280  *
00281  * Purpose: reads the options and sets the defaults.
00282  *
00283  * Arguments: pointer to string with options
00284  *
00285  * Returns: void function
00286  */
00287 void ParseClamAVArgs(u_char *args)
00288 {
00289    char **toks;
00290    int num_toks;
00291    int i = 0;
00292    char *index;
00293    int ports_done = 0;
00294    char **dbdirtoks;
00295    int num_dbdirtoks = 0;
00296    char **dbtimetoks;
00297    int num_dbtimetoks = 0;
00298    char **desctmptoks;
00299    int num_desctmptoks = 0;
00300 
00301 
00302    /* ftp, smtp, http, pop3, nntp, samba (2x), imap */
00303    u_char *default_ports = "21 25 80 81 110 119 139 445 143 8080";
00304 
00305 #ifdef GIDS
00306    clamcnf.drop = 0;
00307    clamcnf.reset = 0;
00308    clamcnf.rboth = 0;
00309 #endif /* GIDS */
00310    clamcnf.toclientonly = 0;
00311    clamcnf.toserveronly = 0;
00312 
00313    /* default tmp dir */
00314    if(strlcpy(clamcnf.desctmpdir, "/tmp", sizeof(clamcnf.desctmpdir)) >= sizeof(clamcnf.desctmpdir))
00315    {
00316        FatalError("The tempdir supplied at compile time is too long\n");
00317    }
00318 
00319 #ifdef CLAMAV_DEFDIR
00320    /* copy the default that was set at compile time, if any */
00321    if(strlcpy(clamcnf.dbdir, xstr(CLAMAV_DEFDIR), sizeof(clamcnf.dbdir)) >= sizeof(clamcnf.dbdir))
00322 #else
00323    /* otherwise a buildin default */
00324    if(strlcpy(clamcnf.dbdir, "/var/lib/clamav/", sizeof(clamcnf.dbdir)) >= sizeof(clamcnf.dbdir))
00325 #endif
00326    {
00327        FatalError("The defdir supplied at compile time is too long\n");
00328    }
00329 
00330 
00331    /* reload time default to 10 minutes */
00332    clamcnf.reloadtime = 600;
00333 
00334 
00335    if(!pv.quiet_flag)
00336    {
00337        LogMessage("ClamAV config:\n");
00338    }
00339 
00340 
00341    /* if no args, load the default config */
00342    if(args == NULL)
00343    {
00344        if(!pv.quiet_flag)
00345        {
00346            LogMessage("    no options, using defaults.\n");
00347        }
00348    }
00349    /* process the args */
00350    else
00351    {
00352        toks = mSplit(args, ",", 12, &num_toks, 0);
00353 
00354        for(i = 0; i < num_toks; i++)
00355        {
00356            index = toks[i];
00357            while(isspace((int)*index)) index++;
00358 
00359            if(!strncasecmp(index, "ports", 5))
00360            {
00361                ProcessPorts(toks[i]);
00362                ports_done = 1;
00363            }
00364 #ifdef GIDS
00365            else if(!strncasecmp(index, "action-reset", 12))
00366            {
00367               clamcnf.reset = 1;
00368            }
00369            else if(!strncasecmp(index, "action-rboth", 12))
00370            {
00371               clamcnf.rboth = 1;
00372            }
00373            else if(!strncasecmp(index, "action-drop", 11))
00374            {
00375               clamcnf.drop = 1;
00376            }
00377 #endif /* GIDS */
00378            else if(!strncasecmp(index, "toclientonly", 12))
00379            {
00380               clamcnf.toclientonly = 1;
00381            }
00382            else if(!strncasecmp(index, "toserveronly", 12))
00383            {
00384               clamcnf.toserveronly = 1;
00385            }
00386            else if(!strncasecmp(index, "dbdir", 5))
00387            {
00388                /* get the argument for the option */
00389                dbdirtoks = mSplit(index, " ", 1, &num_dbdirtoks, 0);
00390 
00391                /* copy it to the clamcnf */
00392                if(strlcpy(clamcnf.dbdir, dbdirtoks[1], sizeof(clamcnf.dbdir)) >= sizeof(clamcnf.dbdir))
00393                {
00394                    FatalError("The defdir supplied in the config is too long\n");
00395                }
00396              mSplitFree(&dbdirtoks, num_dbdirtoks);
00397            }
00398            else if(!strncasecmp(index, "dbreload-time", 13))
00399            {
00400                /* get the argument for the option */
00401                dbtimetoks = mSplit(index, " ", 1, &num_dbtimetoks, 0);
00402 
00403                if(isdigit((int)dbtimetoks[1][0]))
00404                {
00405                    clamcnf.reloadtime  = atoi(dbtimetoks[1]);
00406                }
00407                else
00408                {
00409                    FatalError("We need an integer in seconds for dbreload interval\n");
00410                }
00411              mSplitFree(&dbtimetoks, num_dbtimetoks);
00412            }
00413            else if((!strncasecmp(index, "descriptor-temp-dir", 19)))
00414            {
00415                /* get the argument for the option */
00416                desctmptoks = mSplit(index, " ", 1, &num_desctmptoks, 0);
00417 
00418                /* copy it to the clamcnf */
00419                if(strlcpy(clamcnf.desctmpdir, desctmptoks[1], sizeof(clamcnf.desctmpdir)) >= sizeof(clamcnf.desctmpdir))
00420                {
00421                    FatalError("The tmpdir supplied in the config is too long\n");
00422                }
00423              mSplitFree(&desctmptoks, num_desctmptoks);
00424            }
00425            else
00426            {
00427                FatalError("%s(%d) => Bad ClamAV option specified: "
00428                           "\"%s\"\n", file_name, file_line, toks[i]);
00429            }
00430        }
00431 
00432        mSplitFree(&toks, num_toks);
00433    }
00434 
00435 #ifdef GIDS
00436    /* sanety checks */
00437    if(clamcnf.drop && clamcnf.reset)
00438    {
00439        FatalError("Can't set action-drop and action-reset together!\n");
00440    }
00441 #endif /* GIDS */
00442    if(clamcnf.toclientonly && clamcnf.toserveronly)
00443    {
00444        FatalError("Can't set toclientonly and toserveronly together!\n");
00445    }
00446 
00447 
00448    /* if at this stage the ports are not yet done, load the default ports */
00449    if(!ports_done)
00450        ProcessPorts(default_ports);
00451 
00452 
00453    /* some pretty printing */
00454    if(!pv.quiet_flag)
00455    {
00456        /* action */
00457 #ifdef GIDS
00458        if(clamcnf.drop == 1)
00459            LogMessage("    Virus found action: DROP\n");
00460        else if(clamcnf.reset == 1)
00461            LogMessage("    Virus found action: RESET\n");
00462        else if(clamcnf.rboth == 1)
00463            LogMessage("    Virus found action: RESET-BOTH\n");
00464        else
00465            LogMessage("    Virus found action: ALERT\n");
00466 #endif /* GIDS */
00467        /* dbdir */
00468        LogMessage("    Virus definitions dir: '%s'\n", clamcnf.dbdir);
00469        /* limits */
00470        LogMessage("    Virus DB reload time: '%i'\n", clamcnf.reloadtime);
00471        if(clamcnf.toclientonly == 1)
00472            LogMessage("    Scan only traffic to the client\n");
00473        else if(clamcnf.toserveronly == 1)
00474            LogMessage("    Scan only traffic to the server\n");
00475        LogMessage("    Directory for tempfiles (file descriptor mode): '%s'\n",
00476                            clamcnf.desctmpdir);
00477    }
00478 }
00479 
00480 
00481 /*
00482  * Function: ClamAVInit(u_char *)
00483  *
00484  * All it does right now is register the plugin, eventually we will take port args
00485  */
00486 void ClamAVInit(u_char *args)
00487 {
00488    int ret = 0;
00489    int n = 0;
00490    struct timeval t;
00491    cl_root = NULL;
00492 
00493    /* first parse the commandline args */
00494    ParseClamAVArgs(args);
00495 
00496 
00497    /* get the current time for the reloading */
00498    memset(&t, 0, sizeof(struct timeval));
00499    gettimeofday(&t, NULL);
00500    clamcnf.next_reload_time = t.tv_sec + clamcnf.reloadtime;
00501 
00502    /* setup for check */
00503    memset(&dbstat, 0, sizeof(struct cl_stat));
00504    cl_statinidir(clamcnf.dbdir, &dbstat);
00505 
00506    /* open the defs dir */
00507    ret = cl_loaddbdir(clamcnf.dbdir, &cl_root, &n);
00508    if(ret != 0)
00509    {
00510        FatalError("ClamAV: cl_loaddbdir() %s\n", cl_strerror(ret));
00511    }
00512 
00513    /* run buildtrie */
00514 #ifdef CLAMAV_HAVE_CL_BUILD
00515    if((ret = cl_build(cl_root)))
00516 #else
00517    if((ret = cl_buildtrie(cl_root)))
00518 #endif /* CLAMAV_HAVE_CL_BUILD */
00519    {
00520        FatalError("ClamAV: cl_build() %s\n", cl_strerror(ret));
00521    }
00522 
00523    /* set the limits */
00524    memset(&clam_limits, 0, sizeof(clam_limits));
00525    /* maximal number of files in archive */;
00526    clam_limits.maxfiles = 1000;
00527    /* maximal archived file size */
00528    clam_limits.maxfilesize = 10 * 1048576; /* 10 MB */
00529    /* maximal recursion level */
00530    clam_limits.maxreclevel = 5;
00531    /* maximal compression ratio */
00532    clam_limits.maxratio = 200;
00533    /* disable memory limit for bzip2 scanner */
00534    clam_limits.archivememlim = 0;
00535 
00536    /* register the VirusChecker */
00537    AddFuncToPreprocList(VirusChecker);
00538 }
00539 
00540 
00541 /*
00542  * Function:  ClamAVReloadDB(void)
00543  *
00544  * Purpose:   Determines if the virus definition files need
00545  *            to be reloaded. If so, reload them.
00546  *
00547  * Arguments: pointer to Packet *p, for the time only
00548  *
00549  * Returns:   none, void function
00550  *
00551  */
00552 static void ClamAVReloadDB(Packet *p)
00553 {
00554    int ret = 0;
00555    int n = 0;
00556 
00557    DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"going to Reload ClamAV Database\n"););
00558    clamcnf.next_reload_time = p->pkth->ts.tv_sec + clamcnf.reloadtime;
00559 
00560    /* now really check */
00561    if(cl_statchkdir(&dbstat) == 1)
00562    {
00563        /* okay, we are going to reload the db */
00564 #ifdef CLAMAV_HAVE_CL_BUILD
00565        cl_free(cl_root);
00566 #else
00567        cl_freetrie(cl_root);
00568 #endif /* CLAMAV_HAVE_CL_BUILD */
00569        cl_root = NULL;
00570 
00571        /* open the defs dir */
00572        ret = cl_loaddbdir(clamcnf.dbdir, &cl_root, &n);
00573        if(ret != 0)
00574        {
00575            FatalError("ClamAV: cl_loaddbdir() %s\n", cl_strerror(ret));
00576        }
00577 
00578        /* run buildtrie */
00579 #ifdef CLAMAV_HAVE_CL_BUILD
00580        if((ret = cl_build(cl_root)))
00581 #else
00582        if((ret = cl_buildtrie(cl_root)))
00583 #endif /* CLAMAV_HAVE_CL_BUILD */
00584        {
00585            FatalError("ClamAV: cl_build() %s\n", cl_strerror(ret));
00586        }
00587 
00588        DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"New ClamAV Database loaded\n"););
00589 
00590        /* re-init the dbstat */
00591        cl_statfree(&dbstat);
00592 
00593        memset(&dbstat, 0, sizeof(struct cl_stat));
00594        cl_statinidir(clamcnf.dbdir, &dbstat);
00595    }
00596    else
00597    {
00598        DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"Database is up2date\n"););
00599    }
00600 
00601    return;
00602 }
00603 
00604 
00605 /*
00606  * Function:  check_for_http(Packet *p, u_int8_t **data, int *dsize)
00607  *
00608  * Purpose:   Determines if the packet is a HTTP response. We are interested
00609  *            in those because we want to cut off the header from the
00610  *            response, so ClamAV has less trouble detecting virusses.
00611  *
00612  *            It doesn't actually modify any buffer, it only returns (through
00613  *            the **data argument) the start of the buffer after the header.
00614  *            *dsize is the size of the buffer without the header.
00615  *
00616  *            The check for HTTP should be very cheap, so we do it on all
00617  *            traffic. Later we can change this if there is a need to.
00618  *
00619  * Arguments: pointer to the packet, pointer to a data pointer, pointer to
00620  *            any int.
00621  *
00622  * Returns:   nothing, void function
00623  */
00624 int
00625 strip_http_headers_p(Packet *p, u_int8_t **data, int *dsize)
00626 {
00627        u_int8_t *needle = NULL;
00628        int offset = 0;
00629        int tmpsize = 0;
00630        int chunked = 0;
00631        u_int8_t *newbuf = NULL;
00632   
00633        /* set needle to past the HTTP keyword */
00634        needle = p->data + 5;
00635 
00636        /* search the http headers for Transfer-Encoding */
00637        needle = strstr(needle, "Transfer-Encoding: chunked");
00638 
00639        if(needle != NULL)
00640        {
00641               /* chunked encoding used */
00642               chunked = 1;
00643        }
00644        else
00645        { 
00646            /* needle is NULL set the pointer back */
00647            needle = p->data + 5;
00648        }
00649        /* search for the end of the http header string */
00650        needle = strstr(needle, "\r\n\r\n");
00651        if(needle == NULL)
00652        {
00653                return 0;
00654        }
00655        /* set newbuf past the http headers */
00656        newbuf = needle + 4;
00657        /* size of packet minus headers */ 
00658        offset = newbuf - p->data;
00659 
00660        /* point the new buf to the beginning of the payload */
00661        if(chunked)
00662        {
00663          /* chunked encoding used put needle past */
00664          needle = needle + 4; 
00665          needle = strstr(needle, "\r\n");
00666          if(needle == NULL)
00667          {
00668                /* chunked but couldn't return after chunksize */
00669                return 0;
00670          }
00671          /* just get us past the first chunksize */
00672          newbuf = needle + 2;
00673          
00674          /* size of packet minus headers and chunksize */
00675          offset = newbuf - p->data; 
00676        }
00677 
00678        DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"p->data %p, needle %p, newbuf %p, offset %d, p->dsize %u\n",
00679                p->data, needle, newbuf, offset, p->dsize););
00680 
00681        /* some sanity checks */
00682        if(offset >= p->dsize)
00683                return 0;
00684        if(offset < 0)
00685                return 0;
00686 
00687        tmpsize = p->dsize - offset;
00688 
00689        while(check_4_http_headers(newbuf,tmpsize))
00690        {
00691           needle = newbuf + 5;
00692           /* stripping http headers from buffer */
00693           needle = strstr(needle, "\r\n\r\n");
00694           if(needle == NULL)
00695           {
00696                return 0;
00697           }
00698 
00699           /* point the new buf to the beginning of the payload */
00700           newbuf = needle + 4;
00701           offset = newbuf - p->data;
00702           tmpsize = p->dsize - offset;
00703        }
00704 
00705        /* some sanity checks */
00706        if(offset >= p->dsize)
00707                return 0;
00708        if(offset < 0)
00709                return 0;
00710 
00711        /* finally pass the new buf ptr and size back to the caller */
00712        *data = newbuf;
00713        *dsize = p->dsize - offset;
00714        return 1;
00715 }
00716 
00717 
00718 int
00719 check_4_http_headers(u_int8_t *buf, int dsize)
00720 {
00721     if((dsize > 5) &&
00722         (buf[0] == 'H' || buf[1] == 'T' ||
00723          buf[2] == 'T' || buf[3] == 'P'))
00724     {
00725        /* http headers found */ 
00726        return 1;
00727     }
00728 
00729     //printf("no http headers found\n");
00730     return 0;
00731 }
00732 
00733 
00734 /*
00735  * Function:  ScanPort(Packet *p)
00736  *
00737  * Purpose:   Determines if a packet needs to be scanned based
00738  *            on the source and destination ports, and for tcp
00739  *            packets also if we want to scan only toclient or
00740  *            toserver packets.
00741  *
00742  * Arguments: pointer to the packet
00743  *
00744  * Returns:   returns 1 if the packet needs to be scanned,
00745  *                    0 otherwise.
00746  */
00747 static INLINE int ScanPort(Packet *p)
00748 {
00749    /* tcp packet */
00750    if(p->tcph)
00751    {
00752        if(p->packet_flags & PKT_FROM_SERVER && !clamcnf.toserveronly)
00753        {
00754            /* server to client packet: check sp */
00755            if(!(clamcnf.VirusScanPorts[(p->sp/8)] & (1<<(p->sp%8))))
00756                return 0;
00757            else
00758                return 1;
00759        }
00760        else if(p->packet_flags & PKT_FROM_CLIENT && !clamcnf.toclientonly)
00761        {
00762            /* client to server packet: check dp */
00763            if(!(clamcnf.VirusScanPorts[(p->dp/8)] & (1<<(p->dp%8))))
00764                return 0;
00765            else
00766                return 1;
00767        }
00768        else
00769        {
00770            /* if we get here the packet was FROM_SERVER while in toserveronly mode
00771               or vice-versa. So we don't scan. */
00772            return 0;
00773        }
00774    }
00775    /* if the packet is not tcp we have no idea about the direction of the
00776     * packet. So we check both the dp and the sp. */
00777    else if(!(clamcnf.VirusScanPorts[(p->dp/8)] & (1<<(p->dp%8))) && /* destination ports */
00778            !(clamcnf.VirusScanPorts[(p->sp/8)] & (1<<(p->sp%8))))   /* source ports */
00779    {
00780        return 0;
00781    }
00782    else
00783    {
00784        return 1;
00785    }
00786 }
00787 
00788 
00789 static ssize_t writepacket(int fd, const void *pbuffer, size_t psize)
00790 {
00791    ssize_t pdwritten = 0, pd;
00792 
00793    do {
00794        DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"we have written %i of %i bytes in writepacket loop\n",pdwritten,psize););
00795 
00796        if((pd = write(fd, &((const char *)pbuffer)[pdwritten], psize - pdwritten)) == -1)
00797        {
00798            if(errno == EINTR)
00799            {
00800                DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"we wrote %i of %i bytes to disk and got a signal interrupt looping\n",pdwritten,psize););
00801            }
00802            else
00803            {
00804                return -1;
00805            }
00806        }
00807        pdwritten += pd;
00808 
00809        DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"we have written %i of %i bytes in writepacket loop\n",pdwritten,psize););
00810    } while (pdwritten < psize);
00811 
00812    return pdwritten;
00813 }
00814 
00815 
00816 /*      returns:
00817        1: virus or error, so not scanned
00818        0: ok
00819 */
00820 static INLINE int StoreAndScan(Packet *p)
00821 {
00822    int fd = 0;
00823    char tempfilename[256]="";
00824    size_t size = 0;
00825    int ret = 0, retval = 0;
00826    Event event;
00827    char outstring[255];
00828    const char *cl_virusname = NULL;
00829    u_int8_t *buf = p->data;
00830    int dsize = p->dsize;
00831 
00832    /* create a secure temp file */
00833    size = snprintf(tempfilename, sizeof(tempfilename), "%s/snort_inline-clamav-XXXXXX", clamcnf.desctmpdir);
00834    DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"tempfilename is %s\n",tempfilename););
00835    if(size >= sizeof(tempfilename))
00836    {
00837        FatalError("buffer too small");
00838    }
00839    fd = mkstemp(tempfilename);
00840    if(fd == -1)
00841    {
00842        DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"mkstemp error: %s.\n", strerror(errno)););
00843        return 1;
00844    }
00845 
00846    /* check if this is a HTTP response, if it is, the buffer
00847       and buffer size will be adjusted */
00848    if(check_4_http_headers(buf,dsize))
00849    {
00850       if(strip_http_headers_p(p, &buf, &dsize))
00851       {
00852          //printf("headers stripped\n");
00853       }
00854    }
00855    
00856    /* writing the data to the tempfile */
00857    ret = writepacket(fd, buf, dsize);
00858    if(ret < dsize)
00859    {
00860        DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"tmpfile too small\n"););
00861    }
00862 
00863    /* scan the descriptor */
00864    ret = cl_scandesc(fd, &cl_virusname, NULL, cl_root, &clam_limits, CL_SCAN_STDOPT);
00865    if(ret == CL_CLEAN)
00866    {
00867        DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"Packet is clean\n"););
00868    }
00869    else if(ret == CL_VIRUS)
00870    {
00871        snprintf(outstring, sizeof(outstring), "%s %s", CLAMAV_VIRUSFOUND_STR, cl_virusname);
00872 
00873        DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"tempfile and or packet is infected: '%s'.\n", outstring););
00874 
00875        /* alert! */
00876        SetEvent(&event, GENERATOR_SPP_CLAMAV, CLAMAV_VIRUSFOUND, 1, 0, 0, 0);
00877        CallAlertFuncs(p, outstring, NULL, &event);
00878        CallLogFuncs(p, outstring, NULL, &event);
00879        retval = 1;
00880    }
00881    else
00882    {
00883        /* error */
00884        if(ret < 0)
00885        {
00886            /* close the fd */
00887            close(fd);
00888            /* remove the file */
00889            unlink(tempfilename);
00890            /* ret < 0 is very serious, bail out */
00891            FatalError("ClamAV scan error: %s.\n", cl_strerror(ret));
00892        }
00893        else
00894            /* error, but dont bail out (like we did before), because
00895             * we dont want to be DoS-ed because of a clamav bug.
00896             */
00897            DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"ClamAV scan error: %s.\n", cl_strerror(ret)););
00898    }
00899 
00900    /* close the fd */
00901    close(fd);
00902    /* remove it */
00903    unlink(tempfilename);
00904 
00905    return retval;
00906 }
00907 
00908 
00909 /*
00910  * Function: VirusInPacket(Packet *)
00911  *
00912  * Purpose: Perform Virusscanning on the payload of a packet.
00913  *
00914  * Arguments: p => pointer to the current packet data struct
00915  *
00916  * Returns: 1: is a virus was found
00917  *          0: if the packet is clean
00918  *
00919  */
00920 static int VirusInPacket(Packet *p)
00921 {
00922    /* check if we need to reload the virus db */
00923    if(p->pkth->ts.tv_sec >= clamcnf.next_reload_time)
00924    {
00925        ClamAVReloadDB(p);
00926    }
00927 
00928 
00929    /* virus scanning requires data, so check if we have any */
00930    if(p->dsize == 0)
00931    {
00932        return 0;
00933    }
00934 
00935 
00936    /* check the port list to see if we need to scan this port */
00937    if(!ScanPort(p))
00938    {
00939        return 0;
00940    }
00941 
00942 
00943    /* if cl_root is NULL we return */
00944    if(!cl_root)
00945    {
00946        return 0;
00947    }
00948 
00949 
00950    /* do the actual scanning */
00951    return(StoreAndScan(p));
00952 }
00953 
00954 
00955 /*
00956  * Function: PreprocFunction(Packet *)
00957  *
00958  * Purpose: Perform the preprocessor's intended function.  This can be
00959  *          simple (statistics collection) or complex (IP defragmentation)
00960  *          as you like.  Try not to destroy the performance of the whole
00961  *          system by trying to do too much....
00962  *
00963  * Arguments: p => pointer to the current packet data struct
00964  *
00965  * Returns: void function
00966  *
00967  */
00968 static void VirusChecker(Packet *p, void *context)
00969 {
00970    if(VirusInPacket(p))
00971    {
00972 #ifdef GIDS
00973        if(clamcnf.reset)
00974        {
00975            DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"ClamAV virus found sending Reject\n"););
00976            InlineReject(p);
00977        }
00978        else if(clamcnf.rboth)
00979        {
00980            DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"ClamAV virus found sending Reject\n"););
00981            InlineRejectBoth(p);
00982        }
00983        else if(clamcnf.drop)
00984        {
00985            DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"ClamAV virus found sending Drop\n"););
00986            InlineDrop();
00987        }
00988        else
00989        {
00990             /* we only call AlertFlushStream when we are in alert mode
00991                because if the packet stays in our reassembled buffer
00992                (in stream4inline mode) we might keep alerting on it. */
00993             AlertFlushStream(p);
00994             /* we also disable detection because if snort alerts it will
00995                try to access the reassebled session, which is already gone.
00996                This will cause us to SEGV. */
00997             DisableDetect(p);
00998        }
00999        if((SppStickydIsRunning()) && sdt.clamav)
01000        {
01001            DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"ClamAV adding ip to StickyDrop treei\n"););
01002            AddIpToBlockTree(p, 0, sdt.clamav);
01003        }
01004 #endif /* GIDS */
01005    }
01006    return;
01007 }
01008 
01009 #endif /* CLAMAV */

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