diff -uNr snort-2.8.3.1/autojunk.sh snortsam-2.8.3.1/autojunk.sh --- snort-2.8.3.1/autojunk.sh 1969-12-31 18:00:00.000000000 -0600 +++ snortsam-2.8.3.1/autojunk.sh 2008-11-05 19:08:29.000000000 -0600 @@ -0,0 +1,7 @@ +#!/bin/sh +# the list of commands that need to run before we do a compile +libtoolize --automake --copy +aclocal -I m4 +autoheader +automake --add-missing --copy +autoconf diff -uNr snort-2.8.3.1/src/Makefile.am snortsam-2.8.3.1/src/Makefile.am --- snort-2.8.3.1/src/Makefile.am 2007-11-12 17:10:30.000000000 -0600 +++ snortsam-2.8.3.1/src/Makefile.am 2008-11-05 19:09:15.000000000 -0600 @@ -53,7 +53,8 @@ pcap_pkthdr32.h \ cpuclock.h \ sf_types.h \ -log_text.c log_text.h +log_text.c log_text.h \ +twofish.c twofish.h snort_LDADD = output-plugins/libspo.a \ detection-plugins/libspd.a \ diff -uNr snort-2.8.3.1/src/Makefile.am.orig snortsam-2.8.3.1/src/Makefile.am.orig --- snort-2.8.3.1/src/Makefile.am.orig 1969-12-31 18:00:00.000000000 -0600 +++ snortsam-2.8.3.1/src/Makefile.am.orig 2007-11-12 17:10:30.000000000 -0600 @@ -0,0 +1,74 @@ +## $Id$ +AUTOMAKE_OPTIONS=foreign no-dependencies + +bin_PROGRAMS = snort + +snort_SOURCES = cdefs.h \ +event.h \ +fatal.h \ +generators.h \ +prototypes.h \ +plugin_enum.h \ +rules.h \ +sys_include.h \ +checksum.h \ +codes.c codes.h \ +debug.c debug.h \ +decode.c decode.h \ +log.c log.h \ +mstring.c mstring.h \ +parser.c parser.h \ +profiler.c profiler.h \ +plugbase.c plugbase.h \ +preprocids.h \ +snort.c snort.h \ +build.h \ +snprintf.c snprintf.h \ +strlcatu.c strlcatu.h \ +strlcpyu.c strlcpyu.h \ +tag.c tag.h \ +ubi_BinTree.c ubi_BinTree.h \ +ubi_SplayTree.c ubi_SplayTree.h \ +util.c util.h \ +detect.c detect.h \ +signature.c signature.h \ +mempool.c mempool.h \ +sf_sdlist.c sf_sdlist.h \ +fpcreate.c fpcreate.h \ +fpdetect.c fpdetect.h \ +pcrm.c pcrm.h \ +bounds.h \ +byte_extract.c \ +byte_extract.h \ +timersub.h \ +spo_plugbase.h \ +sfthreshold.c sfthreshold.h \ +packet_time.c packet_time.h \ +event_wrapper.c event_wrapper.h \ +smalloc.h \ +event_queue.c event_queue.h \ +inline.c inline.h \ +ipv6_port.h \ +ppm.c ppm.h \ +pcap_pkthdr32.h \ +cpuclock.h \ +sf_types.h \ +log_text.c log_text.h + +snort_LDADD = output-plugins/libspo.a \ +detection-plugins/libspd.a \ +dynamic-plugins/libdynamic.a \ +preprocessors/libspp.a \ +preprocessors/flow/portscan/libportscan.a \ +preprocessors/flow/libflow.a \ +parser/libparser.a \ +target-based/libtarget_based.a \ +preprocessors/HttpInspect/libhttp_inspect.a \ +preprocessors/Stream5/libstream5.a \ +sfutil/libsfutil.a + +EXAMPLES_DIR = dynamic-examples + +SUBDIRS = sfutil win32 output-plugins detection-plugins dynamic-plugins preprocessors parser dynamic-preprocessors target-based $(EXAMPLES_DIR) + +INCLUDES = @INCLUDES@ diff -uNr snort-2.8.3.1/src/output-plugins/Makefile.am snortsam-2.8.3.1/src/output-plugins/Makefile.am --- snort-2.8.3.1/src/output-plugins/Makefile.am 2007-08-20 12:26:04.000000000 -0500 +++ snortsam-2.8.3.1/src/output-plugins/Makefile.am 2008-11-05 19:08:29.000000000 -0600 @@ -11,6 +11,7 @@ spo_log_tcpdump.h spo_unified.c spo_unified2.c spo_unified.h spo_unified2.h \ spo_log_ascii.c spo_log_ascii.h spo_alert_sf_socket.h spo_alert_sf_socket.c \ spo_alert_prelude.c spo_alert_prelude.h spo_alert_arubaaction.c spo_alert_arubaaction.h \ +spo_alert_fwsam.c spo_alert_fwsam.h \ spo_alert_test.c spo_alert_test.h INCLUDES = @INCLUDES@ diff -uNr snort-2.8.3.1/src/output-plugins/spo_alert_fwsam.c snortsam-2.8.3.1/src/output-plugins/spo_alert_fwsam.c --- snort-2.8.3.1/src/output-plugins/spo_alert_fwsam.c 1969-12-31 18:00:00.000000000 -0600 +++ snortsam-2.8.3.1/src/output-plugins/spo_alert_fwsam.c 2008-11-05 19:12:13.000000000 -0600 @@ -0,0 +1,1380 @@ +/* $id: snortpatchb,v 1.2 2002/10/26 03:32:35 fknobbe Exp $ +** +** spo_alert_fwsam.c +** +** Copyright (c) 2001-2004 Frank Knobbe +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* + * Purpose: + * + * This module sends alerts to a remote service on a host running SnortSam + * (the agent) which will block the intruding IP address on a variety of + * host and network firewalls. + * + * SnortSam also performs checks against a white-list of never-to-be-blocked IP addresses, + * can override block durations (for example for known proxies), and can detect attack conditions + * where too many blocks are received within a defined interval. If an attack is detected + * it will unblock the last x blocks and wait for the attack to end. + * + * See the SnortSam documentation for more information. + * + * + * Output Plugin Parameters: + *************************** + * + * output alert_fwsam: :/ + * + * : The IP address or host name of the host running SnortSam. + * : The port the remote SnortSam service listens on (default 898). + * : The key used for authentication (encryption really) + * of the communication to the remote service. + * + * Examples: + * + * output alert_fwsam: snortsambox/idspassword + * output alert_fwsam: fw1.domain.tld:898/mykey + * output alert_fwsam: 192.168.0.1/borderfw 192.168.1.254/wanfw + * + * + * Rule Options: + *************** + * + * fwsam: who[how],time; + * + * who: src, source, dst, dest, destination + * IP address to be blocked according to snort rule (some rules + * are reversed, i.e. homenet -> any [and you want to block any]). + * src denotes IP to the left of -> and dst denotes IP to the right + * + * how: Optional. In, out, src, dest, either, both, this, conn, connection + * Tells FW-1 to block packets INcoming from host, OUTgoing to host, + * EITHERway, or only THIS connection (IP/Service pair). + * See 'fw sam' for more information. May be ignored by other plugins. + * + * time: Duration of block in seconds. (Accepts 'days', 'months', 'weeks', + * 'years', 'minutes', 'seconds', 'hours'. Alternatively, a value of + * 0, or the keyword PERManent, INFinite, or ALWAYS, will block the + * host permanently. Be careful with this! + * Tells FW-1 (and others) how long to inhibit packets from the host. + * + * Examples: + * + * fwsam: src[either],15min; + * or dst[in], 2 days 4 hours + * or src, 1 hour + * + * (default: src[either],5min) + * + * + * Effect: + * + * Alerts are sent to the remote SnortSam services on Firewall-1 Management Stations + * or other hosts running SnortSam (as required for Cisco Routers and PIX). + * The remote services will invoke the SAM configuration via the fw sam + * command line, or by sending a packet to the SAM port 18183, or by using the official + * OPSEC API calls, or by telnetting into Cisco routers or PIX firewalls. + * The communication over the network is encrypted using two-fish. + * (Implementation ripped from CryptCat by Farm9 with permission.) + * + * Future Plans: + * + * - Custom alert trigger per rule (x alerts in y secs) --> Seems to exist in Snort 1.9 now. + * - Enable/Allow tagged fwsam: arguments to provide different values to + * different stations. --> Seems to be accomplished with custom rule-types + * + * + * Comments: + * + * It seem that above wishes can be implemented with todays setup. Feedback concerning + * these is greatly appreciated. + * +*/ + + +#include "spo_alert_fwsam.h" +#include "twofish.h" +/* external globals from rules.c */ +extern char *file_name; +extern int file_line; +extern OptTreeNode *otn_tmp; +extern PV pv; + + +/* my globals */ + +FWsamList *FWsamStationList=NULL; /* Global (for all alert-types) list of snortsam stations */ +FWsamOptions *FWsamOptionField=NULL; +unsigned long FWsamMaxOptions=0; + + +/* + * Function: AlertFWsamSetup() + * + * Purpose: Registers the output plugin keyword and initialization + * function into the output plugin list. This is the function that + * gets called from InitOutputPlugins() in plugbase.c. + * It also registers itself as a plugin in order to parse every rule + * and to set the appropiate flags from fwsam: option. + * + * Arguments: None. + * + * Returns: void function + * +*/ +void AlertFWsamSetup(void) +{ + /* link the preprocessor keyword to the init function in + the preproc list */ + RegisterOutputPlugin("alert_fwsam", NT_OUTPUT_ALERT, AlertFWsamInit); + RegisterPlugin("fwsam", AlertFWsamOptionInit, OPT_TYPE_ACTION); + +#ifdef FWSAMDEBUG /* This allows debugging of fwsam only */ + LogMessage("DEBUG => [Alert_FWsam](AlertFWsamSetup) Output plugin is plugged in...\n"); +#endif +} + + +/* This function checks if a given snortsam station is already in + * a given list. +*/ +int FWsamStationExists(FWsamStation *who,FWsamList *list) +{ + while(list) + { + if(list->station) { +// if( who->stationip.s_addr==list->station->stationip.s_addr && + if(IP_EQUALITY(&who->stationip, &list->station->stationip) && + who->stationport==list->station->stationport) + return TRUE; + } + list=list->next; + } + return FALSE; +} + +/* + * Function: AlertFWsamInit(char *args) + * + * Purpose: Calls the argument parsing function, performs final setup on data + * structs, links the preproc function into the function list. + * + * Arguments: args => ptr to argument string + * + * Returns: void function + * +*/ +void AlertFWsamInit(char *args) +{ char *ap; + unsigned long statip,cnt,again,i; + char *stathost,*statport,*statpass; + FWsamStation *station; + FWsamList *fwsamlist=NULL; /* alert-type dependent list of snortsam stations */ + FWsamList *listp,*newlistp; + struct hostent *hoste; + char buf[1024]=""; + FILE *fp; + FWsamOptions tempopt; + +#ifdef FWSAMDEBUG + unsigned long hostcnt=0; + + + + LogMessage("DEBUG => [Alert_FWsam](AlertFWsamInit) Output plugin initializing...\n"); +#endif + + pv.alert_plugin_active = 1; + + /* parse the argument list from the rules file */ + + if(args == NULL) + FatalError("ERROR %s (%d) => [Alert_FWsam](AlertFWsamInit) No arguments to alert_fwsam preprocessor!\n", file_name, file_line); + + if(!FWsamOptionField && !FWsamMaxOptions) + { strncpy(buf,pv.config_dir,sizeof(buf)-1); + strncpy(buf+strlen(buf),SID_MAPFILE,sizeof(buf)-strlen(buf)-1); +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam](AlertFWsamSetup) Using file: %s\n",buf); +#endif + fp=fopen(buf,"rt"); + if(!fp) + { strncpy(buf,pv.config_dir,sizeof(buf)-1); + strncpy(buf+strlen(buf),SID_ALT_MAPFILE,sizeof(buf)-strlen(buf)-1); + fp=fopen(buf,"rt"); + } + if(fp) /* Check for presence of map file and read those in, sorted. */ + { LogMessage("INFO => [Alert_FWsam](AlertFWsamSetup) Using sid-map file: %s\n",buf); + + while(FWsamReadLine(buf,sizeof(buf),fp)) + if(*buf) + FWsamMaxOptions++; + if(FWsamMaxOptions) + { if((FWsamOptionField=(FWsamOptions *)malloc(sizeof(FWsamOptions)*FWsamMaxOptions))==NULL) + FatalError("ERROR => [Alert_FWsam](AlertFWsamSetup) malloc failed for OptionField!\n"); + fseek(fp,0,SEEK_SET); + for(cnt=0;cnt1) + { for(again=TRUE,cnt=FWsamMaxOptions-1;cnt>=1 && again;cnt--) + { for(again=FALSE,i=0;iFWsamOptionField[i+1].sid) + { memcpy(&tempopt,&(FWsamOptionField[i]),sizeof(FWsamOptions)); + memcpy(&(FWsamOptionField[i]),&(FWsamOptionField[i+1]),sizeof(FWsamOptions)); + memcpy(&(FWsamOptionField[i+1]),&tempopt,sizeof(FWsamOptions)); + again=TRUE; + } + } + } + } + } + else + FWsamMaxOptions=1; + fclose(fp); + } + else + FWsamMaxOptions=1; + } + + + ap=args; /* start at the beginning of the argument */ + while(*ap && isspace(*ap)) ap++; + while(*ap) + { stathost=ap; /* first argument should be host */ + statport=NULL; + statpass=NULL; + while(*ap && *ap!=':' && *ap!='/' && !isspace(*ap)) ap++; /* find token */ + switch(*ap) + { case ':': *ap++=0; /* grab the port */ + statport=ap; + while(*ap && *ap!='/' && !isspace(*ap)) ap++; + if(*ap!='/') + break; + case '/': *ap++=0; /* grab the key */ + statpass=ap; + while(*ap && !isspace(*ap)) ap++; + default: break; + } + if(*ap) + { *ap++=0; + while(isspace(*ap)) ap++; + } + /* now we have the first host with port and password (key) */ + /* next we check for valid/blank password/port */ + if(statpass!=NULL) + if(!*statpass) + statpass=NULL; + if(statport!=NULL) + if(!*statport) + statport=NULL; + statip=0; + /* now we check if a valid host was specified */ + if(inet_addr(stathost)==INADDR_NONE) + { hoste=gethostbyname(stathost); + if (!hoste) + LogMessage("WARNING %s (%d) => [Alert_FWsam](AlertFWsamInit) Unable to resolve host '%s'!\n",file_name,file_line,stathost); + else + statip=*(unsigned long *)hoste->h_addr; + } + else + { statip=inet_addr(stathost); + if(!statip) + LogMessage("WARNING %s (%d) => [Alert_FWsam](AlertFWsamInit) Invalid host address '%s'!\n",file_name,file_line,stathost); + } + if(statip) + { /* groovie, a valid host. Let's alloc and assemble the structure for it. */ + if((station=(FWsamStation *)malloc(sizeof(FWsamStation)))==NULL) + FatalError("ERROR => [Alert_FWsam](AlertFWsamInit) malloc failed for station!\n"); + +// station->stationip.s_addr=statip; /* the IP address */ + station->stationip.ip32[0] = statip; /* the IP address */ + if(statport!=NULL && atoi(statport)>0) /* if the user specified one */ + station->stationport=atoi(statport); /* use users setting */ + else + station->stationport=FWSAM_DEFAULTPORT; /* set the default port */ + + if(statpass!=NULL) /* if specified by user */ + strncpy(station->stationkey,statpass,TwoFish_KEY_LENGTH); /* use defined key */ + else + station->stationkey[0]=0; + station->stationkey[TwoFish_KEY_LENGTH]=0; /* make sure it's terminated. (damn strncpy...) */ + + strcpy(station->initialkey,station->stationkey); + station->stationfish=TwoFishInit(station->stationkey); + + station->localsocketaddr.sin_port=htons(0); /* let's use dynamic ports for now */ + station->localsocketaddr.sin_addr.s_addr=0; + station->localsocketaddr.sin_family=AF_INET; + station->stationsocketaddr.sin_port=htons(station->stationport); + //station->stationsocketaddr.sin_addr=station->stationip; + station->stationsocketaddr.sin_addr.s_addr=station->stationip.ip32[0]; + station->stationsocketaddr.sin_family=AF_INET; /* load all socket crap and keep for later */ + + do + station->myseqno=rand(); /* the seqno this host will use */ + while(station->myseqno<20 || station->myseqno>65500); + station->mykeymod[0]=rand(); + station->mykeymod[1]=rand(); + station->mykeymod[2]=rand(); + station->mykeymod[3]=rand(); + station->stationseqno=0; /* peer hasn't answered yet. */ + + + if(!FWsamStationExists(station,FWsamStationList)) /* If we don't have the station already in global list....*/ + { if(FWsamCheckIn(station)) /* ...and we can talk to the agent... */ + { if((newlistp=(FWsamList *)malloc(sizeof(FWsamList)))==NULL) + FatalError("ERROR => [Alert_FWsam](AlertFWsamInit) malloc failed for global newlistp!\n"); + newlistp->station=station; + newlistp->next=NULL; + + if(!FWsamStationList) /* ... add it to the global list/ */ + FWsamStationList=newlistp; + else + { listp=FWsamStationList; + while(listp->next) + listp=listp->next; + listp->next=newlistp; + } + } + else + { TwoFishDestroy(station->stationfish); /* if not, we trash it. */ + free(station); + station=NULL; + } + } +#ifdef FWSAMDEBUG + else + LogMessage("DEBUG => [Alert_FWsam](AlertFWsamInit) Host %s:%i already in global list, skipping CheckIn.\n", sfip_ntoa(&station->stationip),station->stationport); +#endif + + if(station) + { if(!FWsamStationExists(station,fwsamlist)) /* If we don't have the station already in local list....*/ + { if((newlistp=(FWsamList *)malloc(sizeof(FWsamList)))==NULL) + FatalError("ERROR => [Alert_FWsam](AlertFWsamInit) malloc failed for local newlistp!\n"); + newlistp->station=station; + newlistp->next=NULL; + + if(!fwsamlist) /* ... add it to the local list/ */ + fwsamlist=newlistp; + else + { listp=fwsamlist; + while(listp->next) + listp=listp->next; + listp->next=newlistp; + } + } + +#ifdef FWSAMDEBUG + else + LogMessage("DEBUG => [Alert_FWsam](AlertFWsamInit) Host %s:%i already in local list, skipping.\n",sfip_ntoa(&station->stationip),station->stationport); + LogMessage("DEBUG => [Alert_FWsam](AlertFWsamInit) #%i: Host %s [%s] port %i password %s\n",++hostcnt,stathost,sfip_ntoa(&station->stationip),station->stationport,station->stationkey); +#endif + } + + } + } /* next one */ + +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam](AlertFWsamInit) Linking fwsam alert function to call list...\n"); +#endif + + /* Set the preprocessor function into the function list */ + AddFuncToOutputList(AlertFWsam, NT_OUTPUT_ALERT, fwsamlist); + AddFuncToCleanExitList(AlertFWsamCleanExitFunc, fwsamlist); + AddFuncToRestartList(AlertFWsamRestartFunc, fwsamlist); +} + + +/* This routine reads in a str from a file, snips white-spaces + * off the front and back, removes comments, and pretties the + * string. Returns true or false if a line was read or not. +*/ +int FWsamReadLine(char *buf,unsigned long bufsize,FILE *fp) +{ char *p; + + if(fgets(buf,bufsize-1,fp)) + { buf[bufsize-1]=0; + +#ifdef FWSAMDEBUG_off + LogMessage("DEBUG => [Alert_FWsam](AlertFWsamReadLine) Line: %s\n",buf); +#endif + + p=buf; + while(isspace(*p)) + p++; + if(p>buf); + strcpy(buf,p); + if(*buf) + { p=buf+strlen(buf)-1; /* remove leading and trailing spaces */ + while(isspace(*p)) + *p-- =0; + } + p=buf; + if(*p=='#' || *p==';') + *p=0; + else + p++; + while(*p) /* remove inline comments (except escaped #'s and ;'s) */ + { if(*p=='#' || *p==';') + { if(*(p-1)=='\\') + strcpy(p-1,p); + else + *p=0; + } + else + p++; + } + return TRUE; + } + return FALSE; +} + + +/* Parses the duration of the argument, recognizing minutes, hours, etc.. +*/ +unsigned long FWsamParseDuration(char *p) +{ unsigned long dur=0,tdu; + char *tok,c1,c2; + + while(*p) + { tok=p; + while(*p && isdigit(*p)) + p++; + if(*p) + { c1=tolower(*p); + *p=0; + p++; + if(*p && !isdigit(*p)) + { c2=tolower(*p++); + while(*p && !isdigit(*p)) + p++; + } + else + c2=0; + tdu=atol(tok); + switch(c1) + { case 'm': if(c2=='o') /* month */ + tdu*=(60*60*24*30); /* use 30 days */ + else + tdu*=60; /* minutes */ + case 's': break; /* seconds */ + case 'h': tdu*=(60*60); /* hours */ + break; + case 'd': tdu*=(60*60*24); /* days */ + break; + case 'w': tdu*=(60*60*24*7); /* week */ + break; + case 'y': tdu*=(60*60*24*365); /* year */ + break; + } + dur+=tdu; + } + else + dur+=atol(tok); + } + + return dur; +} + + +/* This routine parses an option line. It is called by FWsamParseLine, + * which parses the sid-block.map file, and also by AlertFWsamOptionInit, + * which is called by Snort when processing fwsam: options in rules. + * It returns TRUE it there is a possible option problem, otherwise FALSE. +*/ +int FWsamParseOption(FWsamOptions *optp,char *ap) +{ int possprob=FALSE; + + /* set defaults */ + + optp->duration=300; /* default of 5 minute block */ + optp->how=FWSAM_HOW_INOUT; /* inbound and outbound block */ + optp->who=FWSAM_WHO_SRC; /* the source */ + optp->loglevel=FWSAM_LOG_LONGALERT; /* the log level default */ + /* parse the fwsam keywords */ + +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam](AlertFWamOptionInit) Parse Options Args: %s\n",ap); +#endif + + if(*ap) /* should be dst/src (the WHO) or duration */ + { if(isdigit(*ap)) + optp->duration=FWsamParseDuration(ap); + else + { switch(*ap) /* yeah, we're lazy and check only the first character */ + { case 'p': ; /* permanent, perm */ + case 'f': ; /* forever */ + case 'i': optp->duration=0; /* infinite, inf */ + break; + case 'd': optp->who=FWSAM_WHO_DST; /* destination, dest, dst */ + break; + case 's': optp->who=FWSAM_WHO_SRC; /* source, src */ + break; + default: possprob=TRUE; + } + while(*ap && *ap!=',' && *ap!='[') + ap++; + if(*ap=='[') + { ap++; /* now we have the HOW */ + switch(*ap) + { case 'i': ; /* in */ + case 's': optp->how=FWSAM_HOW_IN; /* source, src */ + break; + case 'o': ; /* out */ + case 'd': optp->how=FWSAM_HOW_OUT; /* destination, dest, dst */ + break; + case 'b': ; /* both */ + case 'e': optp->how=FWSAM_HOW_INOUT; /* either */ + break; + case 't': ; /* this */ + case 'c': optp->how=FWSAM_HOW_THIS; /* connection, conn */ + break; + default: possprob=TRUE; + } + while(*ap && *ap!=',') + ap++; + } + if(*ap==',') + { ap++; + if(isdigit(*ap)) /* and figure out how long to block */ + optp->duration=FWsamParseDuration(ap); + else if(*ap=='p' || *ap=='f' || *ap=='i') + optp->duration=0; + else + possprob=TRUE; + } + else if(!*ap) + possprob=TRUE; + } + } + else + possprob=TRUE; + + return possprob; +} + + +/* This goes through the lines of sid-block.map and sets the + * options for fwsam if the file is being used. +*/ +void FWsamParseLine(FWsamOptions *optp,char *buf) +{ char *ap; + + ap=buf; /* start at the beginning of the argument */ + + while(*ap) + { if(isspace(*ap)) /* normalize spaces (tabs into space, etc) */ + *ap=' '; + if(isupper(*ap)) /* and set to lower case */ + *ap=tolower(*ap); + ap++; + } + while((ap=strrchr(buf,' '))!=NULL) /* remove spaces */ + strcpy(ap,ap+1); + + ap=buf; + if(*ap) + { while(*ap && *ap!=':' && *ap!='|') + ap++; + *ap++ =0; + while(*ap && (*ap==':' || *ap=='|')) + ap++; + + optp->sid=(unsigned long)atol(buf); + + if(FWsamParseOption(optp,ap)) + LogMessage("WARNING %s (%d) => [Alert_FWsam](AlertFWamOptionInit) Possible option problem. Using %s[%s],%lu.\n",file_name,file_line,(optp->who==FWSAM_WHO_SRC)?"src":"dst",(optp->how==FWSAM_HOW_IN)?"in":((optp->how==FWSAM_HOW_OUT)?"out":"either"),optp->duration); + } + else + optp->sid=0; +} + + + +/* + * Function: AlertFWsamOptionInit(char *data, OptTreeNode *otn, int protocol) + * + * Purpose: Parses each rule and sets the option flags in the tree. + * + * Arguments: args => ptr to argument string + * + * Returns: void function + * +*/ +void AlertFWsamOptionInit(char *args,OptTreeNode *otn,int protocol) +{ + FWsamOptions *optp; + char *ap; + + +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam](AlertFWamOptionInit) FWsamOptionInit is parsing...\n"); +#endif + + if((optp=(FWsamOptions *)malloc(sizeof(FWsamOptions)))==NULL) + FatalError("ERROR => [Alert_FWsam](AlertFWamOptionInit) malloc failed for opt!\n"); + + + ap=args; /* start at the beginning of the argument */ + + while(*ap) + { if(isspace(*ap)) /* normalize spaces (tabs into space, etc) */ + *ap=' '; + if(isupper(*ap)) /* and set to lower case */ + *ap=tolower(*ap); + ap++; + } + while((ap=strrchr(args,' '))!=NULL) /* remove spaces */ + strcpy(ap,ap+1); + + + if(FWsamParseOption(optp,args)) + LogMessage("WARNING %s (%d) => [Alert_FWsam](AlertFWamOptionInit) Possible option problem. Using %s[%s],%lu.\n",file_name,file_line,(optp->who==FWSAM_WHO_SRC)?"src":"dst",(optp->how==FWSAM_HOW_IN)?"in":((optp->how==FWSAM_HOW_OUT)?"out":"either"),optp->duration); + + otn->ds_list[PLUGIN_FWSAM]=(FWsamOptions *)optp; +} + + +/* Generates a new encryption key for TwoFish based on seq numbers and a random that + * the SnortSam agents send on checkin (in protocol) +*/ +void FWsamNewStationKey(FWsamStation *station,FWsamPacket *packet) +{ + //unsigned char newkey[TwoFish_KEY_LENGTH+2]; + char newkey[TwoFish_KEY_LENGTH+2]; + int i; + + newkey[0]=packet->snortseqno[0]; /* current snort seq # (which both know) */ + newkey[1]=packet->snortseqno[1]; + newkey[2]=packet->fwseqno[0]; /* current SnortSam seq # (which both know) */ + newkey[3]=packet->fwseqno[1]; + newkey[4]=packet->protocol[0]; /* the random SnortSam chose */ + newkey[5]=packet->protocol[1]; + + strncpy(newkey+6,station->stationkey,TwoFish_KEY_LENGTH-6); /* append old key */ + newkey[TwoFish_KEY_LENGTH]=0; + + newkey[0]^=station->mykeymod[0]; /* modify key with key modifiers which were */ + newkey[1]^=station->mykeymod[1]; /* exchanged during the check-in handshake. */ + newkey[2]^=station->mykeymod[2]; + newkey[3]^=station->mykeymod[3]; + newkey[4]^=station->fwkeymod[0]; + newkey[5]^=station->fwkeymod[1]; + newkey[6]^=station->fwkeymod[2]; + newkey[7]^=station->fwkeymod[3]; + + for(i=0;i<=7;i++) + if(newkey[i]==0) + newkey[i]++; + + strcpy(station->stationkey,newkey); + TwoFishDestroy(station->stationfish); + station->stationfish=TwoFishInit(newkey); +} + + +/* This routine will search the option list as defined + * by the sid-block.map file and return a pointer + * to the matching record. +*/ +FWsamOptions *FWsamGetOption(unsigned long sid) +{ signed long i,step,diff,o,o2; + +#ifdef FWSAM_FANCYFETCH /* Fancy-fetch jumps in decreasing n/2 steps and takes much less lookups */ + o=o2= -1; + i=step=FWsamMaxOptions>>1; + while(i>=0 && i1) + step=step>>1; + o2=o; + o=i; + if(diff>0) + i+=step; + else + i-=step; + } +#else /* This is just a sequential list lookup */ + for(i=0;i pointer to the packet data struct + * msg => the message to print in the alert + * + * Returns: void function + * + ***************************************************************************/ +void AlertFWsam(Packet *p, char *msg, void *arg, Event *event) +{ FWsamOptions *optp; + FWsamPacket sampacket; + FWsamStation *station=NULL; + FWsamList *fwsamlist; + SOCKET stationsocket; + int i,len,deletestation,stationtry=0; + //unsigned char *encbuf,*decbuf; + char *encbuf,*decbuf; + static unsigned long lastbsip[FWSAM_REPET_BLOCKS],lastbdip[FWSAM_REPET_BLOCKS], + lastbduration[FWSAM_REPET_BLOCKS],lastbtime[FWSAM_REPET_BLOCKS]; + static unsigned short lastbsp[FWSAM_REPET_BLOCKS],lastbdp[FWSAM_REPET_BLOCKS], + lastbproto[FWSAM_REPET_BLOCKS],lastbpointer; + static unsigned char lastbmode[FWSAM_REPET_BLOCKS]; + unsigned long btime=0; + + + if(otn_tmp==NULL) + { +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam] NULL otn_tmp!\n"); +#endif + return; + } + if(p == NULL) + { +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam] NULL packet!\n"); +#endif + return; + } + if(arg == NULL) + { +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam] NULL arg!\n"); +#endif + return; + } + + /* SnortSam does no IPv6 */ + if (!IS_IP4(p)) { +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam] not acting on non-IP4 packet!\n"); +#endif + return; + } + + optp=NULL; + + if(FWsamOptionField) /* If using the file (field present), let's use that */ + optp=FWsamGetOption(event->sig_id); + + if(!optp) /* If file not present, check if an fwsam option was defined on the triggering rule */ + optp=otn_tmp->ds_list[PLUGIN_FWSAM]; + + if(optp) /* if options specified for this rule */ + { if(!btime) /* if this is the first time this function is */ + { for(i=0;i [Alert_FWsam] Alert -> Msg=\"%s\"\n",msg); + + LogMessage("DEBUG => [Alert_FWsam] Alert -> Option: %s[%s],%lu.\n",(optp->who==FWSAM_WHO_SRC)?"src":"dst",(optp->how==FWSAM_HOW_IN)?"in":((optp->how==FWSAM_HOW_OUT)?"out":"either"),optp->duration); +#endif + + len=TRUE; + btime=(unsigned long)time(NULL); /* get current time */ + /* This is a cheap check to see if the blocking request matches any of the previous requests. */ + for(i=0;ihow==FWSAM_HOW_THIS)? /* if blocking mode SERVICE, check for src and dst */ + ( lastbsip[i]==p->iph->ip_src.s_addr && lastbdip[i]==p->iph->ip_dst.s_addr &&lastbproto[i]==p->iph->ip_proto && + ((p->iph->ip_proto==IPPROTO_TCP || p->iph->ip_proto==IPPROTO_UDP)? /* check port only of TCP or UDP */ +/* ((optp->who==FWSAM_WHO_SRC)?(lastbsp[i]==p->sp):(lastbdp[i]==p->dp)):TRUE) ): */ + lastbdp[i]==p->dp:TRUE) ): + ((optp->how==FWSAM_WHO_SRC)?(lastbsip[i]==p->iph->ip_src.s_addr):(lastbdip[i]==p->iph->ip_dst.s_addr))) && /* otherwise if we block source, only compare source. Same for dest. */ + lastbduration[i]==optp->duration && + (lastbmode[i]&(FWSAM_HOW|FWSAM_WHO))==(optp->how|optp->who) && + (btime-lastbtime[i]<((optp->duration>FWSAM_REPET_TIME)?FWSAM_REPET_TIME:optp->duration))) + { len=FALSE; /* If so, we don't need to block again. */ + } + } + if(len) + { if(++lastbpointer>=FWSAM_REPET_BLOCKS) /* increase repetitive check pointer */ + lastbpointer=0; + lastbsip[lastbpointer]=p->iph->ip_src.s_addr; /* and note packet details */ + lastbdip[lastbpointer]=p->iph->ip_dst.s_addr; + lastbduration[lastbpointer]=optp->duration; + lastbmode[lastbpointer]=optp->how|optp->who|optp->loglevel; + lastbproto[lastbpointer]=p->iph->ip_proto; + if(p->iph->ip_proto==IPPROTO_TCP || p->iph->ip_proto==IPPROTO_UDP) + { lastbsp[lastbpointer]=p->sp; /* set ports if TCP or UDP */ + lastbdp[lastbpointer]=p->dp; + } + lastbtime[lastbpointer]=btime; + + + while(fwsamlist!=NULL) + { station=fwsamlist->station; + //if(station->stationip.s_addr) + if(station->stationip.ip32[0]) + { deletestation=FALSE; + stationtry++; /* first try */ + /* create a socket for the station */ + stationsocket=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if(stationsocket==INVALID_SOCKET) + FatalError("ERROR => [Alert_FWsam] Funky socket error (socket)!\n"); + if(bind(stationsocket,(struct sockaddr *)&(station->localsocketaddr),sizeof(struct sockaddr))) + FatalError("ERROR => [Alert_FWsam] Could not bind socket!\n"); + + /* let's connect to the agent */ + if(connect(stationsocket,(struct sockaddr *)&station->stationsocketaddr,sizeof(struct sockaddr))) + { + LogMessage("WARNING => [Alert_FWsam] Could not send block to host %s. Will try later.\n",sfip_ntoa(&station->stationip)); +#ifdef WIN32 + closesocket(stationsocket); +#else + close(stationsocket); +#endif + stationtry=0; + } + else + { +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam] Connected to host %s.\n",sfip_ntoa(&station->stationip)); +#endif + /* now build the packet */ + station->myseqno+=station->stationseqno; /* increase my seqno by adding agent seq no */ + sampacket.endiancheck=1; /* This is an endian indicator for Snortsam */ + sampacket.snortseqno[0]=(char)station->myseqno; + sampacket.snortseqno[1]=(char)(station->myseqno>>8); + sampacket.fwseqno[0]=(char)station->stationseqno;/* fill station seqno */ + sampacket.fwseqno[1]=(char)(station->stationseqno>>8); + sampacket.status=FWSAM_STATUS_BLOCK; /* set block mode */ + sampacket.version=FWSAM_PACKETVERSION; /* set packet version */ + sampacket.duration[0]=(char)optp->duration; /* set duration */ + sampacket.duration[1]=(char)(optp->duration>>8); + sampacket.duration[2]=(char)(optp->duration>>16); + sampacket.duration[3]=(char)(optp->duration>>24); + sampacket.fwmode=optp->how|optp->who|optp->loglevel; /* set the mode */ + sampacket.dstip[0]=(char)p->iph->ip_dst.s_addr; /* destination IP */ + sampacket.dstip[1]=(char)(p->iph->ip_dst.s_addr>>8); + sampacket.dstip[2]=(char)(p->iph->ip_dst.s_addr>>16); + sampacket.dstip[3]=(char)(p->iph->ip_dst.s_addr>>24); + sampacket.srcip[0]=(char)p->iph->ip_src.s_addr; /* source IP */ + sampacket.srcip[1]=(char)(p->iph->ip_src.s_addr>>8); + sampacket.srcip[2]=(char)(p->iph->ip_src.s_addr>>16); + sampacket.srcip[3]=(char)(p->iph->ip_src.s_addr>>24); + sampacket.protocol[0]=(char)p->iph->ip_proto; /* protocol */ + sampacket.protocol[1]=(char)(p->iph->ip_proto>>8);/* protocol */ + + if(p->iph->ip_proto==IPPROTO_TCP || p->iph->ip_proto==IPPROTO_UDP) + { sampacket.srcport[0]=(char)p->sp; /* set ports */ + sampacket.srcport[1]=(char)(p->sp>>8); + sampacket.dstport[0]=(char)p->dp; + sampacket.dstport[1]=(char)(p->dp>>8); + } + else + sampacket.srcport[0]=sampacket.srcport[1]=sampacket.dstport[0]=sampacket.dstport[1]=0; + + sampacket.sig_id[0]=(char)event->sig_id; /* set signature ID */ + sampacket.sig_id[1]=(char)(event->sig_id>>8); + sampacket.sig_id[2]=(char)(event->sig_id>>16); + sampacket.sig_id[3]=(char)(event->sig_id>>24); + +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam] Sending BLOCK\n"); + LogMessage("DEBUG => [Alert_FWsam] Snort SeqNo: %x\n",station->myseqno); + LogMessage("DEBUG => [Alert_FWsam] Mgmt SeqNo : %x\n",station->stationseqno); + LogMessage("DEBUG => [Alert_FWsam] Status : %i\n",FWSAM_STATUS_BLOCK); + LogMessage("DEBUG => [Alert_FWsam] Mode : %i\n",optp->how|optp->who|optp->loglevel); + LogMessage("DEBUG => [Alert_FWsam] Duration : %li\n",optp->duration); + LogMessage("DEBUG => [Alert_FWsam] Protocol : %i\n",GET_IPH_PROTO(p)); +#ifdef SUP_IP6 + LogMessage("DEBUG => [Alert_FWsam] Src IP : %s\n",sfip_ntoa(GET_SRC_IP(p))); + LogMessage("DEBUG => [Alert_FWsam] Dest IP : %s\n",sfip_ntoa(GET_DST_IP(p))); +#else + LogMessage("DEBUG => [Alert_FWsam] Src IP : %s\n",inet_ntoa(p->iph->ip_src)); + LogMessage("DEBUG => [Alert_FWsam] Dest IP : %s\n",inet_ntoa(p->iph->ip_dst)); +#endif + LogMessage("DEBUG => [Alert_FWsam] Src Port : %i\n",p->sp); + LogMessage("DEBUG => [Alert_FWsam] Dest Port : %i\n",p->dp); + LogMessage("DEBUG => [Alert_FWsam] Sig_ID : %lu\n",event->sig_id); + +#endif + + encbuf=TwoFishAlloc(sizeof(FWsamPacket),FALSE,FALSE,station->stationfish); /* get the encryption buffer */ + len=TwoFishEncrypt((char *)&sampacket,&encbuf,sizeof(FWsamPacket),FALSE,station->stationfish); /* encrypt the packet with current key */ + + if(send(stationsocket,encbuf,len,0)!=len) /* weird...could not send */ + { LogMessage("WARNING => [Alert_FWsam] Could not send to host %s. Will try again later.\n",sfip_ntoa(&station->stationip)); +#ifdef WIN32 + closesocket(stationsocket); +#else + close(stationsocket); +#endif + stationtry=0; + } + else + { i=FWSAM_NETWAIT; +#ifdef WIN32 + ioctlsocket(stationsocket,FIONBIO,&i); /* set non blocking and wait for */ +#else + ioctl(stationsocket,FIONBIO,&i); /* set non blocking and wait for */ +#endif + while(i-- >1) /* the response packet */ + { waitms(10); /* wait for response (default maximum 3 secs */ + if(recv(stationsocket,encbuf,len,0)==len) + i=0; /* if we received packet we set the counter to 0. */ + /* by the time we check with if, it's already dec'ed to -1 */ + } + if(!i) /* id we timed out (i was one, then dec'ed)... */ + { LogMessage("WARNING => [Alert_FWsam] Did not receive response from host %s. Will try again later.\n",sfip_ntoa(&station->stationip)); +#ifdef WIN32 + closesocket(stationsocket); +#else + close(stationsocket); +#endif + stationtry=0; + } + else /* got a packet */ + { decbuf=(char *)&sampacket; /* get the pointer to the packet struct */ + len=TwoFishDecrypt(encbuf,&decbuf,sizeof(FWsamPacket)+TwoFish_BLOCK_SIZE,FALSE,station->stationfish); /* try to decrypt the packet with current key */ + + if(len!=sizeof(FWsamPacket)) /* invalid decryption */ + { strcpy(station->stationkey,station->initialkey); /* try the intial key */ + TwoFishDestroy(station->stationfish); + station->stationfish=TwoFishInit(station->stationkey); /* re-initialize the TwoFish with the intial key */ + len=TwoFishDecrypt(encbuf,&decbuf,sizeof(FWsamPacket)+TwoFish_BLOCK_SIZE,FALSE,station->stationfish); /* try again to decrypt */ + LogMessage("INFO => [Alert_FWsam] Had to use initial key!\n"); + } + if(len==sizeof(FWsamPacket)) /* valid decryption */ + { if(sampacket.version==FWSAM_PACKETVERSION)/* master speaks my language */ + { if(sampacket.status==FWSAM_STATUS_OK || sampacket.status==FWSAM_STATUS_NEWKEY + || sampacket.status==FWSAM_STATUS_RESYNC || sampacket.status==FWSAM_STATUS_HOLD) + { station->stationseqno=sampacket.fwseqno[0] | (sampacket.fwseqno[1]<<8); /* get stations seqno */ + station->lastcontact=(unsigned long)time(NULL); /* set the last contact time (not used yet) */ +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam] Received %s\n",sampacket.status==FWSAM_STATUS_OK?"OK": + sampacket.status==FWSAM_STATUS_NEWKEY?"NEWKEY": + sampacket.status==FWSAM_STATUS_RESYNC?"RESYNC": + sampacket.status==FWSAM_STATUS_HOLD?"HOLD":"ERROR"); + LogMessage("DEBUG => [Alert_FWsam] Snort SeqNo: %x\n",sampacket.snortseqno[0]|(sampacket.snortseqno[1]<<8)); + LogMessage("DEBUG => [Alert_FWsam] Mgmt SeqNo : %x\n",station->stationseqno); + LogMessage("DEBUG => [Alert_FWsam] Status : %i\n",sampacket.status); + LogMessage("DEBUG => [Alert_FWsam] Version : %i\n",sampacket.version); +#endif + if(sampacket.status==FWSAM_STATUS_HOLD) + { i=FWSAM_NETHOLD; /* Stay on hold for a maximum of 60 secs (default) */ + while(i-- >1) /* the response packet */ + { waitms(10); /* wait for response */ + if(recv(stationsocket,encbuf,sizeof(FWsamPacket)+TwoFish_BLOCK_SIZE,0)==sizeof(FWsamPacket)+TwoFish_BLOCK_SIZE) + i=0; /* if we received packet we set the counter to 0. */ + } + if(!i) /* id we timed out (i was one, then dec'ed)... */ + { LogMessage("WARNING => [Alert_FWsam] Did not receive response from host %s. Will try again later.\n",sfip_ntoa(&station->stationip)); + stationtry=0; + sampacket.status=FWSAM_STATUS_ERROR; + } + else /* got a packet */ + { decbuf=(char *)&sampacket; /* get the pointer to the packet struct */ + len=TwoFishDecrypt(encbuf,&decbuf,sizeof(FWsamPacket)+TwoFish_BLOCK_SIZE,FALSE,station->stationfish); /* try to decrypt the packet with current key */ + + if(len!=sizeof(FWsamPacket)) /* invalid decryption */ + { strcpy(station->stationkey,station->initialkey); /* try the intial key */ + TwoFishDestroy(station->stationfish); + station->stationfish=TwoFishInit(station->stationkey); /* re-initialize the TwoFish with the intial key */ + len=TwoFishDecrypt(encbuf,&decbuf,sizeof(FWsamPacket)+TwoFish_BLOCK_SIZE,FALSE,station->stationfish); /* try again to decrypt */ + LogMessage("INFO => [Alert_FWsam] Had to use initial key again!\n"); + } +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam] Received %s\n",sampacket.status==FWSAM_STATUS_OK?"OK": + sampacket.status==FWSAM_STATUS_NEWKEY?"NEWKEY": + sampacket.status==FWSAM_STATUS_RESYNC?"RESYNC": + sampacket.status==FWSAM_STATUS_HOLD?"HOLD":"ERROR"); + LogMessage("DEBUG => [Alert_FWsam] Snort SeqNo: %x\n",sampacket.snortseqno[0]|(sampacket.snortseqno[1]<<8)); + LogMessage("DEBUG => [Alert_FWsam] Mgmt SeqNo : %x\n",station->stationseqno); + LogMessage("DEBUG => [Alert_FWsam] Status : %i\n",sampacket.status); + LogMessage("DEBUG => [Alert_FWsam] Version : %i\n",sampacket.version); +#endif + if(len!=sizeof(FWsamPacket)) /* invalid decryption */ + { ErrorMessage("ERROR => [Alert_FWsam] Password mismatch! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); + deletestation=TRUE; + sampacket.status=FWSAM_STATUS_ERROR; + } + else if(sampacket.version!=FWSAM_PACKETVERSION) /* invalid protocol version */ + { ErrorMessage("ERROR => [Alert_FWsam] Protocol version error! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); + deletestation=TRUE; + sampacket.status=FWSAM_STATUS_ERROR; + } + else if(sampacket.status!=FWSAM_STATUS_OK && sampacket.status!=FWSAM_STATUS_NEWKEY && sampacket.status!=FWSAM_STATUS_RESYNC) + { ErrorMessage("ERROR => [Alert_FWsam] Funky handshake error! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); + deletestation=TRUE; + sampacket.status=FWSAM_STATUS_ERROR; + } + } + } + if(sampacket.status==FWSAM_STATUS_RESYNC) /* if station want's to resync... */ + { strcpy(station->stationkey,station->initialkey); /* ...we use the intial key... */ + memcpy(station->fwkeymod,sampacket.duration,4); /* and note the random key modifier */ + } + if(sampacket.status==FWSAM_STATUS_NEWKEY || sampacket.status==FWSAM_STATUS_RESYNC) + { + FWsamNewStationKey(station,&sampacket); /* generate new TwoFish keys */ +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam] Generated new encryption key...\n"); +#endif + } +#ifdef WIN32 + closesocket(stationsocket); +#else + close(stationsocket); +#endif + stationtry=0; + } + else if(sampacket.status==FWSAM_STATUS_ERROR) /* if SnortSam reports an error on second try, */ + { +#ifdef WIN32 + closesocket(stationsocket); /* something is messed up and ... */ +#else + close(stationsocket); +#endif + if(stationtry>1) /* we ignore that station. */ + { deletestation=TRUE; /* flag for deletion */ + ErrorMessage("ERROR => [Alert_FWsam] Could not renegotiate key! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); + } + else /* if we get an error on the first try, */ + { if(!FWsamCheckIn(station)) /* we first try to check in again. */ + { deletestation=TRUE; + ErrorMessage("ERROR => [Alert_FWsam] Password mismatch! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); + } + } + } + else /* an unknown status means trouble... */ + { ErrorMessage("ERROR => [Alert_FWsam] Funky handshake error! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); +#ifdef WIN32 + closesocket(stationsocket); +#else + close(stationsocket); +#endif + deletestation=TRUE; + } + } + else /* if the SnortSam agent uses a different packet version, we have no choice but to ignore it. */ + { ErrorMessage("ERROR => [Alert_FWsam] Protocol version error! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); +#ifdef WIN32 + closesocket(stationsocket); +#else + close(stationsocket); +#endif + deletestation=TRUE; + } + } + else /* if the intial key failed to decrypt as well, the keys are not configured the same, and we ignore that SnortSam station. */ + { ErrorMessage("ERROR => [Alert_FWsam] Password mismatch! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); +#ifdef WIN32 + closesocket(stationsocket); +#else + close(stationsocket); +#endif + deletestation=TRUE; + } + } + } + free(encbuf); /* release of the TwoFishAlloc'ed encryption buffer */ + } + if(stationtry==0 || deletestation) /* if everything went real well, or real bad... */ + { if(deletestation){ /* If it went bad, we remove the station from the list by marking the IP */ +// station->stationip.s_addr=0; + station->stationip.ip32[0]=0; + } + fwsamlist=fwsamlist->next; + } + } + else + fwsamlist=fwsamlist->next; + } + } + else + { +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam] Skipping repetitive block.\n"); +#endif + } + } +} + +/* FWsamCheckOut will be called when Snort exists. It de-registeres this snort sensor + * from the list of sensor that the SnortSam agent keeps. + */ +void FWsamCheckOut(FWsamStation *station) +{ FWsamPacket sampacket; + SOCKET stationsocket; + int i,len; + char *encbuf,*decbuf; + //unsigned char *encbuf,*decbuf; + + + stationsocket=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if(stationsocket==INVALID_SOCKET) + FatalError("ERROR => [Alert_FWsam](FWsamCheckOut) Funky socket error (socket)!\n"); + if(bind(stationsocket,(struct sockaddr *)&(station->localsocketaddr),sizeof(struct sockaddr))) + FatalError("ERROR => [Alert_FWsam](FWsamCheckOut) Could not bind socket!\n"); + + /* let's connect to the agent */ + if(!connect(stationsocket,(struct sockaddr *)&station->stationsocketaddr,sizeof(struct sockaddr))) + { LogMessage("INFO => [Alert_FWsam](FWsamCheckOut) Disconnecting from host %s.\n",sfip_ntoa(&station->stationip)); + /* now build the packet */ + station->myseqno+=station->stationseqno; /* increase my seqno */ + sampacket.endiancheck=1; + sampacket.snortseqno[0]=(char)station->myseqno; + sampacket.snortseqno[1]=(char)(station->myseqno>>8); + sampacket.fwseqno[0]=(char)station->stationseqno; /* fill station seqno */ + sampacket.fwseqno[1]=(char)(station->stationseqno>>8); + sampacket.status=FWSAM_STATUS_CHECKOUT; /* checking out... */ + sampacket.version=FWSAM_PACKETVERSION; + +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckOut) Sending CHECKOUT\n"); + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckOut) Snort SeqNo: %x\n",station->myseqno); + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckOut) Mgmt SeqNo : %x\n",station->stationseqno); + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckOut) Status : %i\n",sampacket.status); + +#endif + + encbuf=TwoFishAlloc(sizeof(FWsamPacket),FALSE,FALSE,station->stationfish); /* get encryption buffer */ + len=TwoFishEncrypt((char *)&sampacket,&encbuf,sizeof(FWsamPacket),FALSE,station->stationfish); /* encrypt packet with current key */ + + if(send(stationsocket,encbuf,len,0)==len) + { i=FWSAM_NETWAIT; +#ifdef WIN32 + ioctlsocket(stationsocket,FIONBIO,&i); /* set non blocking and wait for */ +#else + ioctl(stationsocket,FIONBIO,&i); /* set non blocking and wait for */ +#endif + while(i-- >1) + { waitms(10); /* ...wait a maximum of 3 secs for response... */ + if(recv(stationsocket,encbuf,len,0)==len) /* ... for the status packet */ + i=0; + } + if(i) /* if we got the packet */ + { decbuf=(char *)&sampacket; + len=TwoFishDecrypt(encbuf,&decbuf,sizeof(FWsamPacket)+TwoFish_BLOCK_SIZE,FALSE,station->stationfish); + + if(len!=sizeof(FWsamPacket)) /* invalid decryption */ + { strcpy(station->stationkey,station->initialkey); /* try initial key */ + TwoFishDestroy(station->stationfish); /* toss this fish */ + station->stationfish=TwoFishInit(station->stationkey); /* re-initialze TwoFish with initial key */ + len=TwoFishDecrypt(encbuf,&decbuf,sizeof(FWsamPacket)+TwoFish_BLOCK_SIZE,FALSE,station->stationfish); /* and try to decrypt again */ + LogMessage("INFO => [Alert_FWsam](FWsamCheckOut) Had to use initial key!\n"); + } + if(len==sizeof(FWsamPacket)) /* valid decryption */ + { if(sampacket.version!=FWSAM_PACKETVERSION) /* but don't really care since we are on the way out */ + ErrorMessage("WARNING => [Alert_FWsam](FWsamCheckOut) Protocol version error! What the hell, we're quitting anyway! :)\n"); + } + else + ErrorMessage("WARNING => [Alert_FWsam](FWsamCheckOut) Password mismatch! What the hell, we're quitting anyway! :)\n"); + } + } + free(encbuf); /* release TwoFishAlloc'ed buffer */ + } + else + LogMessage("WARNING => [Alert_FWsam] Could not connect to host %s for CheckOut. What the hell, we're quitting anyway! :)\n",sfip_ntoa(&station->stationip)); +#ifdef WIN32 + closesocket(stationsocket); +#else + close(stationsocket); +#endif +} + + +/* FWSamFree: Disconnects all FW-1 management stations, + * closes sockets, and frees the structures. + */ +void FWsamFree(FWsamList *list) +{ + FWsamList *next; + + while(list) /* Free pointer list for rule type */ + { + next=list->next; + free(list); + list=next; + } + list=FWsamStationList; + + while(list) /* Free global pointer list and stations */ + { + next=list->next; + if (list->station) + { + if(list->station->stationip.ip32[0]) + //if(list->station->stationip.s_addr) + FWsamCheckOut(list->station); /* Send a Check-Out to SnortSam, */ + + TwoFishDestroy(list->station->stationfish); /* toss the fish, */ + free(list->station); /* free station, */ + } + free(list); /* free pointer, */ + list=next; /* and move to next. */ + } + FWsamStationList=NULL; + if(FWsamOptionField) + free(FWsamOptionField); +} + +void AlertFWsamCleanExitFunc(int signal, void *arg) +{ FWsamList *fwsamlist; + +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam](AlertFWsamCleanExitFunc) Exiting...\n"); +#endif + + fwsamlist=(FWsamList *)arg; + FWsamFree(fwsamlist); /* Free all elements */ +} + +void AlertFWsamRestartFunc(int signal, void *arg) +{ FWsamList *fwsamlist; + +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam](AlertFWsamRestartFunc) Restarting...\n"); +#endif + + fwsamlist=(FWsamList *)arg; + FWsamFree(fwsamlist); /* Free all elements */ +} + +/* This routine registers this Snort sensor with SnortSam. + * It will also change the encryption key based on some variables. + */ +int FWsamCheckIn(FWsamStation *station) +{ int i,len,stationok=TRUE; + FWsamPacket sampacket; + char *encbuf,*decbuf; + //unsigned char *encbuf,*decbuf; + SOCKET stationsocket; + + + /* create a socket for the station */ + stationsocket=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if(stationsocket==INVALID_SOCKET) + FatalError("ERROR => [Alert_FWsam](FWsamCheckIn) Funky socket error (socket)!\n"); + if(bind(stationsocket,(struct sockaddr *)&(station->localsocketaddr),sizeof(struct sockaddr))) + FatalError("ERROR => [Alert_FWsam](FWsamCheckIn) Could not bind socket!\n"); + + i=TRUE; + /* let's connect to the agent */ + if(connect(stationsocket,(struct sockaddr *)&station->stationsocketaddr,sizeof(struct sockaddr))) + LogMessage("WARNING => [Alert_FWsam](FWsamCheckIn) Could not connect to host %s. Will try later.\n",sfip_ntoa(&station->stationip)); + else + { LogMessage("INFO => [Alert_FWsam](FWsamCheckIn) Connected to host %s.\n",sfip_ntoa(&station->stationip)); + /* now build the packet */ + sampacket.endiancheck=1; + sampacket.snortseqno[0]=(char)station->myseqno; /* fill my sequence number number */ + sampacket.snortseqno[1]=(char)(station->myseqno>>8); /* fill my sequence number number */ + sampacket.status=FWSAM_STATUS_CHECKIN; /* let's check in */ + sampacket.version=FWSAM_PACKETVERSION; /* set the packet version */ + memcpy(sampacket.duration,station->mykeymod,4); /* we'll send SnortSam our key modifier in the duration slot */ + /* (the checkin packet is just the plain initial key) */ +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckIn) Sending CheckIn\n"); + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckIn) Snort SeqNo: %x\n",station->myseqno); + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckIn) Mode : %i\n",sampacket.status); + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckIn) Version : %i\n",sampacket.version); +#endif + encbuf=TwoFishAlloc(sizeof(FWsamPacket),FALSE,FALSE,station->stationfish); /* get buffer for encryption */ + len=TwoFishEncrypt((char *)&sampacket,&encbuf,sizeof(FWsamPacket),FALSE,station->stationfish); /* encrypt with initial key */ + if(send(stationsocket,encbuf,len,0)!=len) /* weird...could not send */ + LogMessage("WARNING => [Alert_FWsam](FWsamCheckIn) Could not send to host %s. Will try again later.\n",sfip_ntoa(&station->stationip)); + else + { i=FWSAM_NETWAIT; +#ifdef WIN32 + ioctlsocket(stationsocket,FIONBIO,&i); /* set non blocking and wait for */ +#else + ioctl(stationsocket,FIONBIO,&i); /* set non blocking and wait for */ +#endif + while(i-- >1) + { waitms(10); /* wait a maximum of 3 secs for response */ + if(recv(stationsocket,encbuf,len,0)==len) + i=0; + } + if(!i) /* time up? */ + LogMessage("WARNING => [Alert_FWsam](FWsamCheckIn) Did not receive response from host %s. Will try again later.\n",sfip_ntoa(&station->stationip)); + else + { decbuf=(char *)&sampacket; /* got status packet */ + len=TwoFishDecrypt(encbuf,&decbuf,sizeof(FWsamPacket)+TwoFish_BLOCK_SIZE,FALSE,station->stationfish); /* try to decrypt with initial key */ + if(len==sizeof(FWsamPacket)) /* valid decryption */ + { +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckIn) Received %s\n",sampacket.status==FWSAM_STATUS_OK?"OK": + sampacket.status==FWSAM_STATUS_NEWKEY?"NEWKEY": + sampacket.status==FWSAM_STATUS_RESYNC?"RESYNC": + sampacket.status==FWSAM_STATUS_HOLD?"HOLD":"ERROR"); + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckIn) Snort SeqNo: %x\n",sampacket.snortseqno[0]|(sampacket.snortseqno[1]<<8)); + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckIn) Mgmt SeqNo : %x\n",sampacket.fwseqno[0]|(sampacket.fwseqno[1]<<8)); + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckIn) Status : %i\n",sampacket.status); + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckIn) Version : %i\n",sampacket.version); +#endif + if(sampacket.version==FWSAM_PACKETVERSION) /* master speaks my language */ + { if(sampacket.status==FWSAM_STATUS_OK || sampacket.status==FWSAM_STATUS_NEWKEY || sampacket.status==FWSAM_STATUS_RESYNC) + { station->stationseqno=sampacket.fwseqno[0]|(sampacket.fwseqno[1]<<8); /* get stations seqno */ + station->lastcontact=(unsigned long)time(NULL); + + if(sampacket.status==FWSAM_STATUS_NEWKEY || sampacket.status==FWSAM_STATUS_RESYNC) /* generate new keys */ + { memcpy(station->fwkeymod,sampacket.duration,4); /* note the key modifier */ + FWsamNewStationKey(station,&sampacket); /* and generate new TwoFish keys (with key modifiers) */ +#ifdef FWSAMDEBUG + LogMessage("DEBUG => [Alert_FWsam](FWsamCheckIn) Generated new encryption key...\n"); +#endif + } + } + else /* weird, got a strange status back */ + { ErrorMessage("ERROR => [Alert_FWsam](FWsamCheckIn) Funky handshake error! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); + stationok=FALSE; + } + } + else /* packet version does not match */ + { ErrorMessage("ERROR =>[Alert_FWsam](FWsamCheckIn) Protocol version error! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); + stationok=FALSE; + } + } + else /* key does not match */ + { ErrorMessage("ERROR => [Alert_FWsam](FWsamCheckIn) Password mismatch! Ignoring host %s.\n",sfip_ntoa(&station->stationip)); + stationok=FALSE; + } + } + } + free(encbuf); /* release TwoFishAlloc'ed buffer */ + } +#ifdef WIN32 + closesocket(stationsocket); +#else + close(stationsocket); +#endif + return stationok; +} +#undef FWSAMDEBUG + diff -uNr snort-2.8.3.1/src/output-plugins/spo_alert_fwsam.h snortsam-2.8.3.1/src/output-plugins/spo_alert_fwsam.h --- snort-2.8.3.1/src/output-plugins/spo_alert_fwsam.h 1969-12-31 18:00:00.000000000 -0600 +++ snortsam-2.8.3.1/src/output-plugins/spo_alert_fwsam.h 2008-11-05 19:08:29.000000000 -0600 @@ -0,0 +1,216 @@ +/* $Id: snortpatchb,v 1.5 2005/10/06 08:50:39 fknobbe Exp $ +** +** spo_alert_fwsam.h +** +** Copyright (c) 2001-2004 Frank Knobbe +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* This file gets included in plugbase.c when it is integrated into the rest + * of the program. + * + * For more info, see the beginning of spo_alert_fwsam.c + * + */ + +#ifndef __SPO_FWSAM_H__ +#define __SPO_FWSAM_H__ + +#include "snort.h" +#include "rules.h" +#include "plugbase.h" +#include "plugin_enum.h" +#include "fatal.h" +#include "util.h" +#include "twofish.h" + +#include +#include +#include +#include +#include +#include + + +/* just some compatibility stuff */ +#ifdef WIN32 +#if !defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +#include +#endif +#define waitms(x) Sleep(x) + +#else + +#include +#include +#include +#include +#include + +#ifdef SOLARIS +#include +#endif + +typedef int SOCKET; + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#endif + +#define waitms(x) usleep((x)*1000) + +#endif + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE !FALSE +#endif +#ifndef bool +#define bool int +#endif + + +#if defined(_DEBUG) || defined(DEBUG) +#ifndef FWSAMDEBUG +#define FWSAMDEBUG +#endif +#else +#endif + + +/* Official Snort PlugIn Number has been moved into plugin_enum.h */ + + +/* fixed defines */ + +#define FWSAM_DEFAULTPORT 898 /* Default port if user does not specify one in snort.conf */ + /* (Was unused last time I checked...) */ +#define FWSAM_PACKETVERSION 14 /* version of the packet. Will increase with enhancements. */ + +#define FWSAM_STATUS_CHECKIN 1 /* snort to fw */ +#define FWSAM_STATUS_CHECKOUT 2 +#define FWSAM_STATUS_BLOCK 3 +#define FWSAM_STATUS_UNBLOCK 9 + +#define FWSAM_STATUS_OK 4 /* fw to snort */ +#define FWSAM_STATUS_ERROR 5 +#define FWSAM_STATUS_NEWKEY 6 +#define FWSAM_STATUS_RESYNC 7 +#define FWSAM_STATUS_HOLD 8 + +#define FWSAM_LOG_NONE 0 +#define FWSAM_LOG_SHORTLOG 1 +#define FWSAM_LOG_SHORTALERT 2 +#define FWSAM_LOG_LONGLOG 3 +#define FWSAM_LOG_LONGALERT 4 +#define FWSAM_LOG (FWSAM_LOG_SHORTLOG|FWSAM_LOG_SHORTALERT|FWSAM_LOG_LONGLOG|FWSAM_LOG_LONGALERT) +#define FWSAM_WHO_DST 8 +#define FWSAM_WHO_SRC 16 +#define FWSAM_WHO (FWSAM_WHO_DST|FWSAM_WHO_SRC) +#define FWSAM_HOW_IN 32 +#define FWSAM_HOW_OUT 64 +#define FWSAM_HOW_INOUT (FWSAM_HOW_IN|FWSAM_HOW_OUT) +#define FWSAM_HOW_THIS 128 +#define FWSAM_HOW (FWSAM_HOW_IN|FWSAM_HOW_OUT|FWSAM_HOW_THIS) + + +/* user adjustable defines */ + +#define FWSAM_REPET_BLOCKS 10 /* Snort remembers this amount of last blocks and... */ +#define FWSAM_REPET_TIME 20 /* ...checks if they fall within this time. If so,... */ + /* ...the blocking request is not send. */ + +#define FWSAM_NETWAIT 300 /* 100th of a second. 3 sec timeout for network connections */ +#define FWSAM_NETHOLD 6000 /* 100th of a second. 60 sec timeout for holding */ + +#define SID_MAPFILE "sid-block.map" +#define SID_ALT_MAPFILE "sid-fwsam.map" + +#define FWSAM_FANCYFETCH /* This will invoke a fast sid lookup routine */ + + +/* vars */ + +typedef struct _FWsamstation /* structure of a mgmt station */ +{ unsigned short myseqno; + unsigned short stationseqno; + unsigned char mykeymod[4]; + unsigned char fwkeymod[4]; + unsigned short stationport; + //struct in_addr stationip; + sfip_t stationip; + struct sockaddr_in localsocketaddr; + struct sockaddr_in stationsocketaddr; + TWOFISH *stationfish; + char initialkey[TwoFish_KEY_LENGTH+2]; + char stationkey[TwoFish_KEY_LENGTH+2]; + time_t lastcontact; +/* time_t sleepstart; */ +} FWsamStation; + +typedef struct _FWsampacket /* 2 blocks (3rd block is header from TwoFish) */ +{ unsigned short endiancheck; /* 0 */ + unsigned char srcip[4]; /* 2 */ + unsigned char dstip[4]; /* 6 */ + unsigned char duration[4]; /* 10 */ + unsigned char snortseqno[2]; /* 14 */ + unsigned char fwseqno[2]; /* 16 */ + unsigned char srcport[2]; /* 18 */ + unsigned char dstport[2]; /* 20 */ + unsigned char protocol[2]; /* 22 */ + unsigned char fwmode; /* 24 */ + unsigned char version; /* 25 */ + unsigned char status; /* 26 */ + unsigned char sig_id[4]; /* 27 */ + unsigned char fluff; /* 31 */ +} FWsamPacket; /* 32 bytes in size */ + +typedef struct _FWsamoptions /* snort rule options */ +{ unsigned long sid; + unsigned long duration; + unsigned char who; + unsigned char how; + unsigned char loglevel; +} FWsamOptions; + +typedef struct _FWsamlistpointer +{ FWsamStation *station; + struct _FWsamlistpointer *next; +} FWsamList; + + +/* functions */ +void AlertFWsamSetup(void); +void AlertFWsamInit(char *args); +void AlertFWsamOptionInit(char *args,OptTreeNode *otn,int protocol); +void AlertFWsamCleanExitFunc(int signal, void *arg); +void AlertFWsamRestartFunc(int signal, void *arg); +void AlertFWsam(Packet *p, char *msg, void *arg, Event *event); +int FWsamCheckIn(FWsamStation *station); +void FWsamCheckOut(FWsamStation *station); +void FWsamNewStationKey(FWsamStation *station,FWsamPacket *packet); +void FWsamFixPacketEndian(FWsamPacket *p); +unsigned long FWsamParseDuration(char *p); +void FWsamFree(FWsamList *fwsamlist); +int FWsamStationExists(FWsamStation *who,FWsamList *list); +int FWsamReadLine(char *,unsigned long,FILE *); +void FWsamParseLine(FWsamOptions *,char *); +FWsamOptions *FWsamGetOption(unsigned long); +int FWsamParseOption(FWsamOptions *,char *); + +#endif /* __SPO_FWSAM_H__ */ diff -uNr snort-2.8.3.1/src/plugbase.c snortsam-2.8.3.1/src/plugbase.c --- snort-2.8.3.1/src/plugbase.c 2008-08-12 13:36:27.000000000 -0500 +++ snortsam-2.8.3.1/src/plugbase.c 2008-11-05 19:08:29.000000000 -0600 @@ -119,6 +119,7 @@ #include "output-plugins/spo_alert_arubaaction.h" #endif + #ifdef HAVE_LIBPRELUDE #include "output-plugins/spo_alert_prelude.h" #endif @@ -128,6 +129,7 @@ #endif #include "output-plugins/spo_alert_test.h" +#include "output-plugins/spo_alert_fwsam.h" PluginSignalFuncNode *PluginShutdownList = NULL; PluginSignalFuncNode *PluginCleanExitList = NULL; @@ -1131,6 +1133,7 @@ #endif AlertTestSetup(); + AlertFWsamSetup(); } void CleanupOutputPlugins() diff -uNr snort-2.8.3.1/src/plugbase.c.orig snortsam-2.8.3.1/src/plugbase.c.orig --- snort-2.8.3.1/src/plugbase.c.orig 1969-12-31 18:00:00.000000000 -0600 +++ snortsam-2.8.3.1/src/plugbase.c.orig 2008-08-12 13:36:27.000000000 -0500 @@ -0,0 +1,2033 @@ +/* $Id$ */ +/* +** Copyright (C) 2002-2008 Sourcefire, Inc. +** Copyright (C) 1998-2002 Martin Roesch +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License Version 2 as +** published by the Free Software Foundation. You may not use, modify or +** distribute this program under any other version of the GNU General +** Public License. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#ifdef HAVE_STRINGS_H +#include +#endif + +#ifndef WIN32 +#include +#include +#include +#endif /* !WIN32 */ +#include +#include + + +#include "plugbase.h" +#include "spo_plugbase.h" +#include "snort.h" +#include "debug.h" +#include "util.h" +#include "log.h" +#include "detect.h" + +/* built-in preprocessors */ +#include "preprocessors/spp_rpc_decode.h" +#include "preprocessors/spp_bo.h" +#include "preprocessors/spp_stream4.h" +#include "preprocessors/spp_stream5.h" +#include "preprocessors/spp_arpspoof.h" +#include "preprocessors/spp_perfmonitor.h" +#include "preprocessors/spp_httpinspect.h" +#include "preprocessors/spp_flow.h" +#include "preprocessors/spp_sfportscan.h" +#include "preprocessors/spp_frag3.h" + +/* built-in detection plugins */ +#include "detection-plugins/sp_pattern_match.h" +#include "detection-plugins/sp_tcp_flag_check.h" +#include "detection-plugins/sp_icmp_type_check.h" +#include "detection-plugins/sp_icmp_code_check.h" +#include "detection-plugins/sp_ttl_check.h" +#include "detection-plugins/sp_ip_id_check.h" +#include "detection-plugins/sp_tcp_ack_check.h" +#include "detection-plugins/sp_tcp_seq_check.h" +#include "detection-plugins/sp_dsize_check.h" +#include "detection-plugins/sp_ipoption_check.h" +#include "detection-plugins/sp_rpc_check.h" +#include "detection-plugins/sp_icmp_id_check.h" +#include "detection-plugins/sp_icmp_seq_check.h" +#include "detection-plugins/sp_session.h" +#include "detection-plugins/sp_ip_tos_check.h" +#include "detection-plugins/sp_ip_fragbits.h" +#include "detection-plugins/sp_tcp_win_check.h" +#include "detection-plugins/sp_ip_same_check.h" +#include "detection-plugins/sp_ip_proto.h" +#include "detection-plugins/sp_ip_same_check.h" +#include "detection-plugins/sp_clientserver.h" +#include "detection-plugins/sp_byte_check.h" +#include "detection-plugins/sp_byte_jump.h" +#include "detection-plugins/sp_isdataat.h" +#include "detection-plugins/sp_pcre.h" +#include "detection-plugins/sp_flowbits.h" +#include "detection-plugins/sp_asn1.h" +#if defined(ENABLE_RESPONSE) && !defined(ENABLE_RESPONSE2) +#include "detection-plugins/sp_react.h" +#include "detection-plugins/sp_respond.h" +#elif defined(ENABLE_RESPONSE2) && !defined(ENABLE_RESPONSE) +#include "detection-plugins/sp_respond2.h" +#endif +#if defined(ENABLE_REACT) && !defined(ENABLE_RESPONSE) +#include "detection-plugins/sp_react.h" +#endif +#include "detection-plugins/sp_ftpbounce.h" +#include "detection-plugins/sp_urilen_check.h" +#include "detection-plugins/sp_cvs.h" + +/* built-in output plugins */ +#include "output-plugins/spo_alert_syslog.h" +#include "output-plugins/spo_log_tcpdump.h" +#include "output-plugins/spo_database.h" +#include "output-plugins/spo_alert_fast.h" +#include "output-plugins/spo_alert_full.h" +#include "output-plugins/spo_alert_unixsock.h" +#include "output-plugins/spo_csv.h" +#include "output-plugins/spo_unified.h" +#include "output-plugins/spo_log_null.h" +#include "output-plugins/spo_log_ascii.h" +#include "output-plugins/spo_unified2.h" + +#ifdef ARUBA +#include "output-plugins/spo_alert_arubaaction.h" +#endif + +#ifdef HAVE_LIBPRELUDE +#include "output-plugins/spo_alert_prelude.h" +#endif + +#ifdef LINUX +#include "output-plugins/spo_alert_sf_socket.h" +#endif + +#include "output-plugins/spo_alert_test.h" + +PluginSignalFuncNode *PluginShutdownList = NULL; +PluginSignalFuncNode *PluginCleanExitList = NULL; +PluginSignalFuncNode *PluginRestartList = NULL; +PluginSignalFuncNode *PluginPostConfigList = NULL; + +PreprocSignalFuncNode *PreprocShutdownList = NULL; +PreprocSignalFuncNode *PreprocCleanExitList = NULL; +PreprocSignalFuncNode *PreprocRestartList = NULL; +PreprocSignalFuncNode *PreprocResetList = NULL; +PreprocSignalFuncNode *PreprocResetStatsList = NULL; +PreprocGetReassemblyPktFuncNode *PreprocGetReassemblyPktList = NULL; + +extern int file_line; +extern char *file_name; + + +/**************************** Detection Plugin API ****************************/ +KeywordXlateList *KeywordList; + +void InitPlugIns() +{ + if(!pv.quiet_flag) + { + LogMessage("Initializing Plug-ins!\n"); + } + SetupPatternMatch(); + SetupTCPFlagCheck(); + SetupIcmpTypeCheck(); + SetupIcmpCodeCheck(); + SetupTtlCheck(); + SetupIpIdCheck(); + SetupTcpAckCheck(); + SetupTcpSeqCheck(); + SetupDsizeCheck(); + SetupIpOptionCheck(); + SetupRpcCheck(); + SetupIcmpIdCheck(); + SetupIcmpSeqCheck(); + SetupSession(); + SetupIpTosCheck(); + SetupFragBits(); + SetupFragOffset(); + SetupTcpWinCheck(); + SetupIpProto(); + SetupIpSameCheck(); + SetupClientServer(); + SetupByteTest(); + SetupByteJump(); + SetupIsDataAt(); + SetupPcre(); + SetupFlowBits(); + SetupAsn1(); +#if defined(ENABLE_RESPONSE) && !defined(ENABLE_RESPONSE2) + SetupReact(); + SetupRespond(); +#elif defined(ENABLE_RESPONSE2) && !defined(ENABLE_RESPONSE) + SetupRespond2(); +#endif +#if defined(ENABLE_REACT) && !defined(ENABLE_RESPONSE) + SetupReact(); +#endif + SetupFTPBounce(); + SetupUriLenCheck(); + SetupCvs(); +} + +void CleanupPlugInSigList(PluginSignalFuncNode *sig) +{ + PluginSignalFuncNode *tmpSig; + while (sig) + { + tmpSig = sig->next; + /* don't free sig->arg, that's free'd by the CleanExit/Restart func */ + free(sig); + sig = tmpSig; + } +} + +void CleanupPlugIns() +{ + KeywordXlateList *tmpIdx, *idx = KeywordList; + + while (idx) + { + tmpIdx = idx->next; + if (idx->entry.keyword) + free(idx->entry.keyword); + free(idx); + idx = tmpIdx; + } + + CleanupPlugInSigList(PluginShutdownList); + PluginShutdownList = NULL; + + CleanupPlugInSigList(PluginCleanExitList); + PluginCleanExitList = NULL; + + CleanupPlugInSigList(PluginRestartList); + PluginRestartList = NULL; + + CleanupPlugInSigList(PluginPostConfigList); + PluginPostConfigList = NULL; +} + +/**************************************************************************** + * utils for translation from enum to char* + ***************************************************************************/ + +#ifdef DEBUG +static const char* optTypeMap[OPT_TYPE_MAX] = { + "action", "logging", "detection" +}; +#endif + +#define ENUM2STR(num, map) \ + ((num < sizeof(map)/sizeof(map[0])) ? map[num] : "undefined") + +/**************************************************************************** + * + * Function: RegisterPlugin(char *, void (*func)(), enum OptionType) + * + * Purpose: Associates a rule option keyword with an option setup/linking + * function. + * + * Arguments: keyword => The option keyword to associate with the option + * handler + * *func => function pointer to the handler + * type => used to determine where keyword is allowed + * + * Returns: void function + * + ***************************************************************************/ +void RegisterPlugin( + char *keyword, + void (*func) (char *, OptTreeNode *, int), + enum OptionType type +) { + KeywordXlateList *idx; + + DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Registering keyword:func => %s/%s:%p\n", + ENUM2STR(type, optTypeMap), keyword, func);); + + idx = KeywordList; + + if(idx == NULL) + { + KeywordList = (KeywordXlateList *)SnortAlloc(sizeof(KeywordXlateList)); + + KeywordList->entry.keyword = (char *)SnortAlloc((strlen(keyword) + 1) * sizeof(char)); + SnortStrncpy(KeywordList->entry.keyword, keyword, strlen(keyword) + 1); + + KeywordList->entry.func = func; + KeywordList->entry.type = type; + } + else + { + /* go to the end of the list */ + while(idx->next != NULL) + { + if(!strcasecmp(idx->entry.keyword, keyword)) + { + FatalError("RegisterPlugin: Duplicate detection plugin keyword:" + " (%s) (%s)!\n", idx->entry.keyword, keyword); + } + idx = idx->next; + } + + idx->next = (KeywordXlateList *)SnortAlloc(sizeof(KeywordXlateList)); + idx = idx->next; + + idx->entry.keyword = (char *)SnortAlloc((strlen(keyword) + 1) * sizeof(char)); + SnortStrncpy(idx->entry.keyword, keyword, strlen(keyword) + 1); + + idx->entry.func = func; + idx->entry.type = type; + } +} + + +/**************************************************************************** + * + * Function: DumpPlugIns() + * + * Purpose: Prints the keyword->function list + * + * Arguments: None. + * + * Returns: void function + * + ***************************************************************************/ +void DumpPlugIns() +{ + KeywordXlateList *idx; + + if(pv.quiet_flag) + return; + + idx = KeywordList; + + LogMessage("-------------------------------------------------\n"); + LogMessage(" Keyword | Plugin Registered @\n"); + LogMessage("-------------------------------------------------\n"); + while(idx != NULL) + { + LogMessage("%-13s: %p\n", idx->entry.keyword, idx->entry.func); + idx = idx->next; + } + LogMessage("-------------------------------------------------\n\n"); +} + + +/**************************************************************************** + * + * Function: AddOptFuncToList(int (*func)(), OptTreeNode *) + * + * Purpose: Links the option detection module to the OTN + * + * Arguments: (*func)() => function pointer to the detection module + * otn => pointer to the current OptTreeNode + * + * Returns: void function + * + ***************************************************************************/ +OptFpList *AddOptFuncToList( +#ifdef DETECTION_OPTION_TREE + int (*func) (void *option_data, Packet *p), +#else + int (*func) (Packet *, struct _OptTreeNode *, struct _OptFpList *), +#endif + OptTreeNode * otn) +{ + OptFpList *idx; /* index pointer */ + + DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Adding new rule to list\n");); + + /* set the index pointer to the start of this OTN's function list */ + idx = otn->opt_func; + + /* if there are no nodes on the function list... */ + if(idx == NULL) + { + /* calloc the list head */ + otn->opt_func = (OptFpList *)calloc(1, sizeof(OptFpList)); + + if(otn->opt_func == NULL) + { + FatalError("new node calloc failed: %s\n", + strerror(errno)); + } + + /* set the head function */ + otn->opt_func->OptTestFunc = func; + + idx = otn->opt_func; + } + else + { + /* walk to the end of the list */ + while(idx->next != NULL) + { + idx = idx->next; + } + + /* allocate a new node on the end of the list */ + idx->next = (OptFpList *)calloc(1, sizeof(OptFpList)); + + if(idx->next == NULL) + { + FatalError("AddOptFuncToList new node calloc failed: %s\n", + strerror(errno)); + } + + /* move up to the new node */ + idx = idx->next; + + /* link the function to the new node */ + idx->OptTestFunc = func; + + DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Set OptTestFunc to %p\n", + func);); + } + + return idx; +} + +/**************************************************************************** + * + * Function: AddRspFuncToList(int (*func)(), OptTreeNode *) + * + * Purpose: Adds Response function to OTN + * + * Arguments: (*func)() => function pointer to the response module + * otn => pointer to the current OptTreeNode + * + * Returns: void function + * + ***************************************************************************/ +void AddRspFuncToList(int (*func) (Packet *, struct _RspFpList *), OptTreeNode * otn, void *params) +{ + RspFpList *idx; /* index pointer */ + + DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Adding response to list\n");); + + /* set the index pointer to the start of this OTN's function list */ + idx = otn->rsp_func; + + /* if there are no nodes on the function list... */ + if(idx == NULL) + { + /* calloc the list head */ + otn->rsp_func = (RspFpList *)calloc(1, sizeof(RspFpList)); + + if(otn->rsp_func == NULL) + { + FatalError("AddRspFuncToList new node calloc failed: %s\n", strerror(errno)); + } + /* set the head function */ + otn->rsp_func->ResponseFunc = func; + otn->rsp_func->params = params; + } + else + { + /* walk to the end of the list */ + while(idx->next != NULL) + { + idx = idx->next; + } + + /* allocate a new node on the end of the list */ + idx->next = (RspFpList *)calloc(1, sizeof(RspFpList)); + + if(idx->next == NULL) + { + FatalError("AddRspFuncToList new node calloc failed: %s\n", strerror(errno)); + } + /* link the function to the new node */ + idx->next->ResponseFunc = func; + idx->next->params = params; + + DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Set ResponseFunc to %p\n", func);); + } +} + + +/************************* End Detection Plugin API ***************************/ + + +/************************** Preprocessor Plugin API ***************************/ +PreprocessKeywordList *PreprocessKeywords = NULL; +PreprocessStatsList *PreprocessStats = NULL; +PreprocessFuncNode *PreprocessList = NULL; +PreprocessCheckConfigNode *PreprocessConfigCheckList = NULL; +static SFGHASH *preprocIdTable = NULL; +unsigned int num_preprocs = 0; + +void InitPreprocessors() +{ + if(!pv.quiet_flag) + { + LogMessage("Initializing Preprocessors!\n"); + } + SetupRpcDecode(); + SetupBo(); +// SetupTelNeg(); + SetupStream4(); + SetupARPspoof(); + SetupHttpInspect(); + SetupPerfMonitor(); + SetupFlow(); + SetupPsng(); + SetupFrag3(); + SetupStream5(); +} + +void CleanupPreprocessorSigList(PreprocSignalFuncNode *sig) +{ + PreprocSignalFuncNode *tmpSig; + while (sig) + { + tmpSig = sig->next; + /* don't free sig->arg, that's free'd by the CleanExit/Restart func */ + free(sig); + sig = tmpSig; + } +} + +void CleanupPreprocessors() +{ + PreprocessKeywordList *tmpIdx, *idx = PreprocessKeywords; + PreprocessCheckConfigNode *tmpConf, *conf = PreprocessConfigCheckList; + PreprocessStatsList *tmpStats, *stats = PreprocessStats; + PreprocessFuncNode *tmpFunc, *func = PreprocessList; + PreprocGetReassemblyPktFuncNode *tmpReass, *reass = PreprocGetReassemblyPktList; + + while (idx) + { + tmpIdx = idx->next; + if (idx->entry.keyword) + free(idx->entry.keyword); + free(idx); + idx = tmpIdx; + } + PreprocessKeywords = NULL; + + while (conf) + { + tmpConf = conf->next; + free(conf); + conf = tmpConf; + } + PreprocessConfigCheckList = NULL; + + while (stats) + { + tmpStats = stats->next; + if (stats->entry.keyword) + free(stats->entry.keyword); + free(stats); + stats = tmpStats; + } + PreprocessStats = NULL; + + while (func) + { + tmpFunc = func->next; + if (func->context) + free(func->context); + free(func); + func = tmpFunc; + } + PreprocessList = NULL; + + CleanupPreprocessorSigList(PreprocShutdownList); + PreprocShutdownList = NULL; + + CleanupPreprocessorSigList(PreprocCleanExitList); + PreprocCleanExitList = NULL; + + CleanupPreprocessorSigList(PreprocRestartList); + PreprocRestartList = NULL; + + CleanupPreprocessorSigList(PreprocResetList); + PreprocResetList = NULL; + + CleanupPreprocessorSigList(PreprocResetStatsList); + PreprocResetStatsList = NULL; + + while (reass) + { + tmpReass = reass->next; + free(reass); + reass = tmpReass; + } + PreprocGetReassemblyPktList = NULL; + + if (preprocIdTable) + sfghash_delete(preprocIdTable); +} + +void CheckPreprocessorsConfig() +{ + PreprocessCheckConfigNode *idx; + + idx = PreprocessConfigCheckList; + + if(!pv.quiet_flag) + { + LogMessage("Verifying Preprocessor Configurations!\n"); + } + + while(idx != NULL) + { + idx->func(); + idx = idx->next; + } +} + +void PostConfigInitPlugins() +{ + PluginSignalFuncNode *idx; + + idx = PluginPostConfigList; + + while (idx != NULL) + { + idx->func(0, idx->arg); + idx = idx->next; + } +} + +/**************************************************************************** + * + * Function: RegisterPreprocessor(char *, void (*)(char *)) + * + * Purpose: Associates a preprocessor statement with its function. + * + * Arguments: keyword => The option keyword to associate with the + * preprocessor + * *func => function pointer to the handler + * + * Returns: void function + * + ***************************************************************************/ +void RegisterPreprocessor(char *keyword, void (*func)(char *)) +{ + PreprocessKeywordList *idx; + + DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Registering keyword:preproc => %s:%p\n", keyword, func);); + + idx = PreprocessKeywords; + + if(idx == NULL) + { + /* alloc the node */ + PreprocessKeywords = (PreprocessKeywordList *)SnortAlloc(sizeof(PreprocessKeywordList)); + + /* alloc space for the keyword */ + PreprocessKeywords->entry.keyword = (char *)SnortAlloc((strlen(keyword) + 1) * sizeof(char)); + + /* copy the keyword into the struct */ + SnortStrncpy(PreprocessKeywords->entry.keyword, keyword, strlen(keyword) + 1); + + /* set the function pointer to the keyword handler function */ + PreprocessKeywords->entry.func = (void (*)(char *))func; + } + else + { + /* loop to the end of the list */ + while(idx->next != NULL) + { + if(!strcasecmp(idx->entry.keyword, keyword)) + { + FatalError("%s(%d) => Duplicate preprocessor keyword!\n", + file_name, file_line); + } + + idx = idx->next; + } + + idx->next = (PreprocessKeywordList *)SnortAlloc(sizeof(PreprocessKeywordList)); + + idx = idx->next; + + /* alloc space for the keyword */ + idx->entry.keyword = (char *)SnortAlloc((strlen(keyword) + 1) * sizeof(char)); + + /* copy the keyword into the struct */ + SnortStrncpy(idx->entry.keyword, keyword, strlen(keyword) + 1); + + /* set the function pointer to the keyword handler function */ + idx->entry.func = func; + } +} + + +/**************************************************************************** + * + * Function: RegisterPreprocStats(char *keyword, void (*func)(int)) + * + * Purpose: Registers a function for printing preprocessor final stats + * (or other if it has a use for printing final stats) + * + * Arguments: keyword => keyword (preprocessor) whose stats will print + * func => function pointer to the handler + * + * Returns: void function + * + ***************************************************************************/ +void RegisterPreprocStats(char *keyword, void (*func)(int)) +{ + PreprocessStatsList *idx; + + DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Registering final stats function: preproc => %s:%p\n", keyword, func);); + + idx = PreprocessStats; + + if (idx == NULL) + { + /* alloc the node */ + PreprocessStats = (PreprocessStatsList *)SnortAlloc(sizeof(PreprocessStatsList)); + + /* alloc space for the keyword */ + PreprocessStats->entry.keyword = (char *)SnortAlloc((strlen(keyword) + 1) * sizeof(char)); + + /* copy the keyword into the struct */ + SnortStrncpy(PreprocessStats->entry.keyword, keyword, strlen(keyword) + 1); + + /* set the function pointer to the keyword handler function */ + PreprocessStats->entry.func = func; + + PreprocessStats->next = NULL; + } + else + { + /* loop to the end of the list */ + while (idx->next != NULL) + { + if(!strcasecmp(idx->entry.keyword, keyword)) + { + FatalError("%s(%d) => Duplicate final stats keyword!\n", + file_name, file_line); + } + + idx = idx->next; + } + + idx->next = (PreprocessStatsList *)SnortAlloc(sizeof(PreprocessStatsList)); + + idx = idx->next; + + /* alloc space for the keyword */ + idx->entry.keyword = (char *)SnortAlloc((strlen(keyword) + 1) * sizeof(char)); + + /* copy the keyword into the struct */ + SnortStrncpy(idx->entry.keyword, keyword, strlen(keyword) + 1); + + /* set the function pointer to the keyword handler function */ + idx->entry.func = func; + + idx->next = NULL; + } +} + + +/*************************************************************************** + * + * Function: PreprocessStatsFree(void) + * + * Purpose: Free memory allocated by RegisterPreprocStats + * + * Arguments: none + * + * Returns: void function + * + **************************************************************************/ +void PreprocessStatsFree(void) +{ + PreprocessStatsList *tmp; + + DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Freeing stats function structures\n");); + + while (PreprocessStats != NULL) + { + if (PreprocessStats->entry.keyword != NULL) + free(PreprocessStats->entry.keyword); + tmp = PreprocessStats; + PreprocessStats = PreprocessStats->next; + free(tmp); + } +} + + +/**************************************************************************** + * + * Function: DumpPreprocessors() + * + * Purpose: Prints the keyword->preprocess list + * + * Arguments: None. + * + * Returns: void function + * + ***************************************************************************/ +void DumpPreprocessors() +{ + PreprocessKeywordList *idx; + + if(pv.quiet_flag) + return; + idx = PreprocessKeywords; + + LogMessage("-------------------------------------------------\n"); + LogMessage(" Keyword | Preprocessor @ \n"); + LogMessage("-------------------------------------------------\n"); + while(idx != NULL) + { + LogMessage("%-13s: %p\n", idx->entry.keyword, idx->entry.func); + idx = idx->next; + } + LogMessage("-------------------------------------------------\n\n"); +} + +int IsPreprocBitSet(Packet *p, unsigned int preproc_bit) +{ +#if 0 + int preproc_bit; + PreprocessFuncNode *idx = sfghash_find(preprocIdTable, &preproc_id); + if (idx) + { + preproc_bit = idx->preproc_bit; + return boIsBitSet(p->preprocessor_bits, preproc_bit); + } + return 0; +#endif + return boIsBitSet(p->preprocessor_bits, preproc_bit); +} + +int SetPreprocBit(Packet *p, unsigned int preproc_id) +{ + int preproc_bit; + PreprocessFuncNode *idx = sfghash_find(preprocIdTable, &preproc_id); + if (idx) + { + preproc_bit = idx->preproc_bit; + return boSetBit(p->preprocessor_bits, preproc_bit); + } + return 0; +} + +int IsPreprocGetReassemblyPktBitSet(Packet *p, unsigned int preproc_id) +{ + PreprocessFuncNode *idx = sfghash_find(preprocIdTable, &preproc_id); + if (idx) + { + int preproc_bit = idx->preproc_bit; + return boIsBitSet(p->preproc_reassembly_pkt_bits, preproc_bit); + } + + return 0; +} + +int SetPreprocGetReassemblyPktBit(Packet *p, unsigned int preproc_id) +{ + PreprocessFuncNode *idx = sfghash_find(preprocIdTable, &preproc_id); + if (idx) + { + int preproc_bit = idx->preproc_bit; + p->packet_flags |= PKT_PREPROC_RPKT; + return boSetBit(p->preproc_reassembly_pkt_bits, preproc_bit); + } + + return 0; +} + +PreprocessFuncNode *AddFuncToPreprocList(void (*func) (Packet *, void *), + unsigned short priority, + unsigned int preproc_id) +{ + PreprocessFuncNode *idx; + PreprocessFuncNode *tmpNext; + PreprocessFuncNode *insertAfter = NULL; + + DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, + "Adding preprocessor function ID %d/bit %d/pri %d to list\n", + preproc_id, num_preprocs, priority);); + idx = PreprocessList; + + if(idx == NULL) + { + PreprocessList = (PreprocessFuncNode *)SnortAlloc(sizeof(PreprocessFuncNode)); + + PreprocessList->func = func; + PreprocessList->priority = priority; + PreprocessList->preproc_id = preproc_id; + PreprocessList->preproc_bit = num_preprocs++; + + idx = PreprocessList; + } + else + { + do + { + if (idx->preproc_id == preproc_id) + { + FatalError("Preprocessor already registered with ID %d\n", preproc_id); + //return NULL; + } + + if (idx->priority > priority) + break; + insertAfter = idx; + idx = idx->next; + } + while (idx); + + idx = (PreprocessFuncNode *)SnortAlloc(sizeof(PreprocessFuncNode)); + if (insertAfter) + { + tmpNext = insertAfter->next; + insertAfter->next = idx; + idx->next = tmpNext; + } + else + { + idx->next = PreprocessList; + PreprocessList = idx; + } + + idx->func = func; + idx->priority = priority; + idx->preproc_id = preproc_id; + idx->preproc_bit = num_preprocs++; + } + + return idx; +} + +void MapPreprocessorIds() +{ + PreprocessFuncNode *idx; + if (preprocIdTable || !num_preprocs) + return; + + preprocIdTable = sfghash_new(num_preprocs, 4, 1, NULL); + + idx = PreprocessList; + + while (idx) + { + DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, + "Adding preprocessor ID %d/bit %d/pri %d to hash table\n", + idx->preproc_id, idx->preproc_bit, idx->priority);); + sfghash_add(preprocIdTable, &(idx->preproc_id), idx); + idx = idx->next; + } +} + +PreprocessCheckConfigNode *AddFuncToConfigCheckList(void (*func)(void)) +{ + PreprocessCheckConfigNode *idx; + + idx = PreprocessConfigCheckList; + + if(idx == NULL) + { + PreprocessConfigCheckList = (PreprocessCheckConfigNode *)SnortAlloc(sizeof(PreprocessCheckConfigNode)); + + PreprocessConfigCheckList->func = func; + + idx = PreprocessConfigCheckList; + } + else + { + while(idx->next != NULL) + idx = idx->next; + + idx->next = (PreprocessCheckConfigNode *)SnortAlloc(sizeof(PreprocessCheckConfigNode)); + + idx = idx->next; + idx->func = func; + } + + return idx; +} + +/* functions to aid in cleaning up after plugins */ +void AddFuncToPreprocRestartList(void (*func) (int, void *), void *arg, + unsigned short priority, unsigned int preproc_id) +{ + PreprocRestartList = AddFuncToPreprocSignalList(func, arg, PreprocRestartList, priority, preproc_id); +} + +void AddFuncToPreprocCleanExitList(void (*func) (int, void *), void *arg, + unsigned short priority, unsigned int preproc_id) +{ + PreprocCleanExitList = AddFuncToPreprocSignalList(func, arg, PreprocCleanExitList, priority, preproc_id); +} + +void AddFuncToPreprocShutdownList(void (*func) (int, void *), void *arg, + unsigned short priority, unsigned int preproc_id) +{ + PreprocShutdownList = AddFuncToPreprocSignalList(func, arg, PreprocShutdownList, priority, preproc_id); +} + +void AddFuncToPreprocResetList(void (*func) (int, void *), void *arg, + unsigned short priority, unsigned int preproc_id) +{ + PreprocResetList = AddFuncToPreprocSignalList(func, arg, PreprocResetList, priority, preproc_id); +} + +void AddFuncToPreprocResetStatsList(void (*func) (int, void *), void *arg, + unsigned short priority, unsigned int preproc_id) +{ + PreprocResetStatsList = AddFuncToPreprocSignalList(func, arg, PreprocResetStatsList, priority, preproc_id); +} + +void AddFuncToPreprocGetReassemblyPktList(void * (*func)(void), unsigned int preproc_id) +{ + PreprocGetReassemblyPktFuncNode *idx = PreprocGetReassemblyPktList; + PreprocGetReassemblyPktFuncNode *tmp; + + if (idx == NULL) + { + idx = (PreprocGetReassemblyPktFuncNode *) + SnortAlloc(sizeof(PreprocGetReassemblyPktFuncNode)); + + idx->func = func; + idx->preproc_id = preproc_id; + } + else + { + /* just insert at front of list */ + tmp = idx; + idx = (PreprocGetReassemblyPktFuncNode *) + SnortAlloc(sizeof(PreprocGetReassemblyPktFuncNode)); + + idx->next = tmp; + idx->func = func; + idx->preproc_id = preproc_id; + } + + PreprocGetReassemblyPktList = idx; +} + +PreprocSignalFuncNode *AddFuncToPreprocSignalList(void (*func) (int, void *), void *arg, + PreprocSignalFuncNode * list, unsigned short priority, + unsigned int preproc_id) +{ + PreprocSignalFuncNode *idx; + PreprocSignalFuncNode *insertAfter = NULL; + PreprocSignalFuncNode *tmpNext; + + idx = list; + + if(idx == NULL) + { + idx = (PreprocSignalFuncNode *)SnortAlloc(sizeof(PreprocSignalFuncNode)); + + idx->func = func; + idx->arg = arg; + idx->preproc_id = preproc_id; + idx->priority = priority; + list = idx; + } + else + { + do + { + if (idx->priority > priority) + break; + + insertAfter = idx; + idx = idx->next; + } + while(idx); + + idx = (PreprocSignalFuncNode *)SnortAlloc(sizeof(PreprocSignalFuncNode)); + if (insertAfter) + { + tmpNext = insertAfter->next; + insertAfter->next = idx; + idx->next = tmpNext; + } + else + { + idx->next = list; + list = idx; + } + + idx->func = func; + idx->arg = arg; + idx->priority = priority; + idx->preproc_id = preproc_id; + } + + return list; +} + +/************************ End Preprocessor Plugin API ************************/ + +/***************************** Output Plugin API *****************************/ +OutputKeywordList *OutputKeywords; +OutputFuncNode *AlertList; +OutputFuncNode *LogList; +OutputFuncNode *AppendOutputFuncList(void (*) (Packet *,char *,void *,Event*), + void *, OutputFuncNode *); + +void InitOutputPlugins() +{ + if(!pv.quiet_flag) + { + LogMessage("Initializing Output Plugins!\n"); + } + AlertSyslogSetup(); + LogTcpdumpSetup(); + DatabaseSetup(); + AlertFastSetup(); + AlertFullSetup(); +#ifndef WIN32 + /* Win32 doesn't support AF_UNIX sockets */ + AlertUnixSockSetup(); +#endif /* !WIN32 */ + AlertCSVSetup(); + LogNullSetup(); + UnifiedSetup(); + Unified2Setup(); + LogAsciiSetup(); + +#ifdef ARUBA + AlertArubaActionSetup(); +#endif + +#ifdef LINUX + /* This uses linux only capabilities */ + AlertSFSocket_Setup(); +#endif + +#ifdef HAVE_LIBPRELUDE + AlertPreludeSetup(); +#endif + + AlertTestSetup(); +} + +void CleanupOutputPlugins() +{ + OutputKeywordList *tmpIdx, *idx = OutputKeywords; + OutputFuncNode *tmpAlert, *alert = AlertList; + OutputFuncNode *tmpLog, *log = LogList; + while (idx) + { + tmpIdx = idx->next; + if (idx->entry.keyword) + free(idx->entry.keyword); + free(idx); + idx = tmpIdx; + } + + while (alert) + { + tmpAlert = alert->next; + free(alert); + alert = tmpAlert; + } + AlertList = NULL; + + while (log) + { + tmpLog = log->next; + free(log); + log = tmpLog; + } + LogList = NULL; +} + +int ActivateOutputPlugin(char *plugin_name, char *plugin_options) +{ + OutputKeywordNode *plugin; + + if(!plugin_name) + return -1; + + /* get the output plugin node */ + plugin = GetOutputPlugin(plugin_name); + if (!plugin) + return -1; + + switch(plugin->node_type) + { + case NT_OUTPUT_SPECIAL: /* both alert & logging in one plugin */ + plugin->func(plugin_options); + break; + case NT_OUTPUT_ALERT: + plugin->func(plugin_options); + break; + case NT_OUTPUT_LOG: + plugin->func(plugin_options); + break; + } + + return 0; +} + +OutputKeywordNode *GetOutputPlugin(char *plugin_name) +{ + OutputKeywordList *list_node = NULL; + OutputKeywordNode *ret = NULL; + + if (!plugin_name) + return NULL; + + list_node = OutputKeywords; + + while (list_node != NULL) + { + if (strcasecmp(plugin_name, list_node->entry.keyword) == 0) + { + ret = &(list_node->entry); + break; + } + + list_node = list_node->next; + } + + if (ret == NULL) + { + FatalError("unknown output plugin: '%s'", plugin_name); + } + + return ret; +} + + +/**************************************************************************** + * + * Function: RegisterOutputPlugin(char *, void (*func)(Packet *, u_char *)) + * + * Purpose: Associates an output statement with its function. + * + * Arguments: keyword => The output keyword to associate with the + * output processor + * type => alert or log types + * *func => function pointer to the handler + * + * Returns: void function + * + ***************************************************************************/ +void RegisterOutputPlugin(char *keyword, int type, OutputInitFunc func) +{ + OutputKeywordList *idx; + + DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Registering keyword:output => %s:%p\n", + keyword, func);); + + idx = OutputKeywords; + + if(idx == NULL) + { + /* alloc the node */ + OutputKeywords = (OutputKeywordList *)SnortAlloc(sizeof(OutputKeywordList)); + + idx = OutputKeywords; + } + else + { + /* loop to the end of the list */ + while(idx->next != NULL) + { + if(!strcasecmp(idx->entry.keyword, keyword)) + { + FatalError("%s(%d) => Duplicate output keyword!\n", + file_name, file_line); + } + idx = idx->next; + } + + idx->next = (OutputKeywordList *)SnortAlloc(sizeof(OutputKeywordList)); + + idx = idx->next; + } + + /* alloc space for the keyword */ + idx->entry.keyword = (char *)SnortAlloc((strlen(keyword) + 1) * sizeof(char)); + + /* copy the keyword into the struct */ + SnortStrncpy(idx->entry.keyword, keyword, strlen(keyword) + 1); + + /* + * set the plugin type, needed to determine whether an overriding command + * line arg has been specified + */ + idx->entry.node_type = (char) type; + + /* set the function pointer to the keyword handler function */ + idx->entry.func = func; +} + + +/**************************************************************************** + * + * Function: DumpOutputPlugins() + * + * Purpose: Prints the keyword->preprocess list + * + * Arguments: None. + * + * Returns: void function + * + ***************************************************************************/ +void DumpOutputPlugins() +{ + OutputKeywordList *idx; + + if(pv.quiet_flag) + return; + + idx = OutputKeywords; + + LogMessage("-------------------------------------------------\n"); + LogMessage(" Keyword | Output @ \n"); + LogMessage("-------------------------------------------------\n"); + while(idx != NULL) + { + LogMessage("%-13s: %p\n", idx->entry.keyword, idx->entry.func); + idx = idx->next; + } + LogMessage("-------------------------------------------------\n\n"); +} + +extern ListHead *head_tmp; + +void AddFuncToOutputList(void (*func) (Packet *, char *, void *, Event *), + char node_type, void *arg) +{ + switch(node_type) + { + case NT_OUTPUT_ALERT: + if(head_tmp != NULL) + head_tmp->AlertList = AppendOutputFuncList(func, arg, + head_tmp->AlertList); + else + AlertList = AppendOutputFuncList(func, arg, AlertList); + break; + + case NT_OUTPUT_LOG: + if(head_tmp != NULL) + head_tmp->LogList = AppendOutputFuncList(func, arg, + head_tmp->LogList); + else + LogList = AppendOutputFuncList(func, arg, LogList); + break; + + default: + /* just to be error-prone */ + FatalError("Unknown nodetype: %i. Possible bug, please report\n", + node_type); + } + + return; +} + + +OutputFuncNode *AppendOutputFuncList( + void (*func) (Packet *, char *, void *, Event *), + void *arg, OutputFuncNode * list) +{ + OutputFuncNode *idx = list; + + if(idx == NULL) + { + idx = (OutputFuncNode *)SnortAlloc(sizeof(OutputFuncNode)); + idx->func = func; + idx->arg = arg; + list = idx; + } + else + { + while(idx->next != NULL) + idx = idx->next; + + idx->next = (OutputFuncNode *)SnortAlloc(sizeof(OutputFuncNode)); + idx = idx->next; + idx->func = func; + idx->arg = arg; + } + + idx->next = NULL; + + return list; +} + +/* + * frees the existing OutputList ands sets it a single node for the + * function argument + */ +void SetOutputList(void (*func) (Packet *, char *, void *, Event *), + char node_type, void *arg) +{ + OutputFuncNode *idx; + OutputFuncNode *prev; + + switch(node_type) + { + case NT_OUTPUT_ALERT: + prev = AlertList; + break; + + case NT_OUTPUT_LOG: + prev = LogList; + break; + + default: + return; + } + + while(prev != NULL) + { + idx = prev->next; + free(prev); + prev = idx; + } + + switch(node_type) + { + case NT_OUTPUT_ALERT: + AlertList = prev; + break; + + case NT_OUTPUT_LOG: + LogList = prev; + break; + + default: + return; + } + + AddFuncToOutputList(func, node_type, arg); + + return; +} + + + +/*************************** End Output Plugin API ***************************/ + + +/************************** Miscellaneous Functions **************************/ + +int PacketIsIP(Packet * p) +{ + if(IPH_IS_VALID(p)) + return 1; + + return 0; +} + + + +int PacketIsTCP(Packet * p) +{ + if(IPH_IS_VALID(p) && p->tcph != NULL) + return 1; + + return 0; +} + + + +int PacketIsUDP(Packet * p) +{ + if(IPH_IS_VALID(p) && p->udph != NULL) + return 1; + + return 0; +} + + + +int PacketIsICMP(Packet * p) +{ + if(IPH_IS_VALID(p) && p->icmph != NULL) + return 1; + + return 0; +} + + + +int DestinationIpIsHomenet(Packet * p) +{ +#ifdef SUP_IP6 + if(sfip_contains(&pv.homenet, GET_DST_IP(p)) == SFIP_CONTAINS) +#else + if((p->iph->ip_dst.s_addr & pv.netmask) == pv.homenet) +#endif + { + return 1; + } + return 0; +} + + + +int SourceIpIsHomenet(Packet * p) +{ +#ifdef SUP_IP6 + if(sfip_contains(&pv.homenet, GET_SRC_IP(p)) == SFIP_CONTAINS) +#else + if((p->iph->ip_src.s_addr & pv.netmask) == pv.homenet) +#endif + { + return 1; + } + return 0; +} + +int CheckNet(struct in_addr * compare, struct in_addr * compare2) +{ + if(compare->s_addr == compare2->s_addr) + { + return 1; + } + return 0; +} + +/* functions to aid in cleaning up after plugins */ +void AddFuncToRestartList(void (*func) (int, void *), void *arg) +{ + PluginRestartList = AddFuncToSignalList(func, arg, PluginRestartList); +} + +void AddFuncToCleanExitList(void (*func) (int, void *), void *arg) +{ + PluginCleanExitList = AddFuncToSignalList(func, arg, PluginCleanExitList); +} + +void AddFuncToShutdownList(void (*func) (int, void *), void *arg) +{ + PluginShutdownList = AddFuncToSignalList(func, arg, PluginShutdownList); +} + +void AddFuncToPostConfigList(void (*func)(int, void *), void *arg) +{ + PluginPostConfigList = AddFuncToSignalList(func, arg, PluginPostConfigList); +} + +PluginSignalFuncNode *AddFuncToSignalList(void (*func) (int, void *), void *arg, + PluginSignalFuncNode * list) +{ + PluginSignalFuncNode *idx; + + idx = list; + + if(idx == NULL) + { + idx = (PluginSignalFuncNode *)SnortAlloc(sizeof(PluginSignalFuncNode)); + + idx->func = func; + idx->arg = arg; + list = idx; + } + else + { + while(idx->next != NULL) + idx = idx->next; + + idx->next = (PluginSignalFuncNode *)SnortAlloc(sizeof(PluginSignalFuncNode)); + + idx = idx->next; + idx->func = func; + idx->arg = arg; + } + idx->next = NULL; + + return list; +} + + +/**************************************************************************** + * + * Function: GetUniqueName(char * iface) + * + * Purpose: To return a string that has a high probability of being unique + * for a given sensor. + * + * Arguments: char * iface - The network interface you are sniffing + * + * Returns: A char * -- its a static char * so you should not free it + * + ***************************************************************************/ +char *GetUniqueName(char * iface) +{ + char * rptr; + static char uniq_name[256]; + + if (iface == NULL) LogMessage("Interface is NULL. Name may not be unique for the host\n"); +#ifndef WIN32 + rptr = GetIP(iface); + if(rptr == NULL || !strcmp(rptr, "unknown")) +#endif + { + SnortSnprintf(uniq_name, 255, "%s:%s\n",GetHostname(),iface); + rptr = uniq_name; + } + if (pv.verbose_flag) LogMessage("Node unique name is: %s\n", rptr); + return rptr; +} + +/**************************************************************************** + * + * Function: GetIP(char * iface) + * + * Purpose: To return a string representing the IP address for an interface + * + * Arguments: char * iface - The network interface you want to find an IP + * address for. + * + * Returns: A char * -- make sure you call free on this when you are done + * with it. + * + ***************************************************************************/ +char *GetIP(char * iface) +{ + struct ifreq ifr; + struct sockaddr_in *addr; + int s; +#ifdef SUP_IP6 + sfip_t ret; +#endif + + if(iface) + { + /* Set up a dummy socket just so we can use ioctl to find the + ip address of the interface */ + s = socket(PF_INET, SOCK_DGRAM, 0); + if(s == -1) + { + FatalError("Problem establishing socket to find IP address for interface: %s\n", iface); + } + + SnortStrncpy(ifr.ifr_name, iface, strlen(iface) + 1); + +#ifndef WIN32 + if(ioctl(s, SIOCGIFADDR, &ifr) < 0) return NULL; + else +#endif + { + addr = (struct sockaddr_in *) &ifr.ifr_broadaddr; + } + close(s); + +#ifdef SUP_IP6 +// XXX-IPv6 uses ioctl to populate a sockaddr_in structure ... but what if the interface only has an IPv6 address? + sfip_set_raw(&ret, addr, AF_INET); + return SnortStrdup(sfip_ntoa(&ret)); +#else + return SnortStrdup(inet_ntoa(addr->sin_addr)); +#endif + } + else + { + return "unknown"; + } +} + +/**************************************************************************** + * + * Function: GetHostname() + * + * Purpose: To return a string representing the hostname + * + * Arguments: None + * + * Returns: A static char * representing the hostname. + * + ***************************************************************************/ +char *GetHostname() +{ +#ifdef WIN32 + DWORD bufflen = 256; + static char buff[256]; + GetComputerName(buff, &bufflen); + return buff; +#else + char * error = "unknown"; + if(getenv("HOSTNAME")) return getenv("HOSTNAME"); + else if(getenv("HOST")) return getenv("HOST"); + else return error; +#endif +} + +/**************************************************************************** + * + * Function: GetTimestamp(register const struct timeval *tvp, int tz) + * + * Purpose: Get an ISO-8601 formatted timestamp for tvp within the tz + * timezone. + * + * Arguments: tvp is a timeval pointer. tz is a timezone. + * + * Returns: char * -- You must free this char * when you are done with it. + * + ***************************************************************************/ +char *GetTimestamp(register const struct timeval *tvp, int tz) +{ + struct tm *lt; /* localtime */ + char * buf; + int msec; + + buf = (char *)SnortAlloc(SMALLBUFFER * sizeof(char)); + + msec = tvp->tv_usec / 1000; + + if(pv.use_utc == 1) + { + lt = gmtime((time_t *)&tvp->tv_sec); + SnortSnprintf(buf, SMALLBUFFER, "%04i-%02i-%02i %02i:%02i:%02i.%03i", + 1900 + lt->tm_year, lt->tm_mon + 1, lt->tm_mday, + lt->tm_hour, lt->tm_min, lt->tm_sec, msec); + } + else + { + lt = localtime((time_t *)&tvp->tv_sec); + SnortSnprintf(buf, SMALLBUFFER, + "%04i-%02i-%02i %02i:%02i:%02i.%03i+%03i", + 1900 + lt->tm_year, lt->tm_mon + 1, lt->tm_mday, + lt->tm_hour, lt->tm_min, lt->tm_sec, msec, tz); + } + + return buf; +} + +/**************************************************************************** + * + * Function: GetLocalTimezone() + * + * Purpose: Find the offset from GMT for current host + * + * Arguments: none + * + * Returns: int representing the offset from GMT + * + ***************************************************************************/ +int GetLocalTimezone() +{ + time_t ut; + struct tm * ltm; + long seconds_away_from_utc; + + time(&ut); + ltm = localtime(&ut); + +#if defined(WIN32) || defined(SOLARIS) || defined(AIX) + /* localtime() sets the global timezone variable, + which is defined in */ + seconds_away_from_utc = timezone; +#else + seconds_away_from_utc = ltm->tm_gmtoff; +#endif + + return seconds_away_from_utc/3600; +} + +/**************************************************************************** + * + * Function: GetCurrentTimestamp() + * + * Purpose: Generate an ISO-8601 formatted timestamp for the current time. + * + * Arguments: none + * + * Returns: char * -- You must free this char * when you are done with it. + * + ***************************************************************************/ +char *GetCurrentTimestamp() +{ + struct tm *lt; + struct timezone tz; + struct timeval tv; + struct timeval *tvp; + char * buf; + int tzone; + int msec; + + buf = (char *)SnortAlloc(SMALLBUFFER * sizeof(char)); + + bzero((char *)&tz,sizeof(tz)); + gettimeofday(&tv,&tz); + tvp = &tv; + + msec = tvp->tv_usec/1000; + + if(pv.use_utc == 1) + { + lt = gmtime((time_t *)&tvp->tv_sec); + SnortSnprintf(buf, SMALLBUFFER, "%04i-%02i-%02i %02i:%02i:%02i.%03i", + 1900 + lt->tm_year, lt->tm_mon + 1, lt->tm_mday, + lt->tm_hour, lt->tm_min, lt->tm_sec, msec); + } + else + { + lt = localtime((time_t *)&tvp->tv_sec); + + tzone = GetLocalTimezone(); + + SnortSnprintf(buf, SMALLBUFFER, + "%04i-%02i-%02i %02i:%02i:%02i.%03i+%03i", + 1900 + lt->tm_year, lt->tm_mon + 1, lt->tm_mday, + lt->tm_hour, lt->tm_min, lt->tm_sec, msec, tzone); + } + + return buf; +} + +/**************************************************************************** + * Function: base64(char * xdata, int length) + * + * Purpose: Insert data into the database + * + * Arguments: xdata => pointer to data to base64 encode + * length => how much data to encode + * + * Make sure you allocate memory for the output before you pass + * the output pointer into this function. You should allocate + * (1.5 * length) bytes to be safe. + * + * Returns: data base64 encoded as a char * + * + ***************************************************************************/ +char * base64(const u_char * xdata, int length) +{ + int count, cols, bits, c, char_count; + unsigned char alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* 64 bytes */ + char * payloadptr; + char * output; + char_count = 0; + bits = 0; + cols = 0; + + output = (char *)SnortAlloc( ((unsigned int) (length * 1.5 + 4)) * sizeof(char) ); + + payloadptr = output; + + for(count = 0; count < length; count++) + { + c = xdata[count]; + + if(c > 255) + { + ErrorMessage("plugbase.c->base64(): encountered char > 255 (decimal %d)\n If you see this error message a char is more than one byte on your machine\n This means your base64 results can not be trusted", c); + } + + bits += c; + char_count++; + + if(char_count == 3) + { + *output = alpha[bits >> 18]; output++; + *output = alpha[(bits >> 12) & 0x3f]; output++; + *output = alpha[(bits >> 6) & 0x3f]; output++; + *output = alpha[bits & 0x3f]; output++; + cols += 4; + if(cols == 72) + { + *output = '\n'; output++; + cols = 0; + } + bits = 0; + char_count = 0; + } + else + { + bits <<= 8; + } + } + + if(char_count != 0) + { + bits <<= 16 - (8 * char_count); + *output = alpha[bits >> 18]; output++; + *output = alpha[(bits >> 12) & 0x3f]; output++; + if(char_count == 1) + { + *output = '='; output++; + *output = '='; output++; + } + else + { + *output = alpha[(bits >> 6) & 0x3f]; + output++; *output = '='; + output++; + } + } + *output = '\0'; + return payloadptr; +} + +/**************************************************************************** + * + * Function: ascii(u_char *xdata, int length) + * + * Purpose: This function takes takes a buffer "xdata" and its length then + * returns a string of only the printible ASCII characters. + * + * Arguments: xdata is the buffer, length is the length of the buffer in + * bytes + * + * Returns: char * -- You must free this char * when you are done with it. + * + ***************************************************************************/ +char *ascii(const u_char *xdata, int length) +{ + char *d_ptr, *ret_val; + int i,count = 0; + int size; + + if(xdata == NULL) + { + return NULL; + } + + for(i=0;i') /* > */ + count += 4; + } + + size = length + count + 1; + ret_val = (char *) calloc(1,size); + + if(ret_val == NULL) + { + LogMessage("plugbase.c: ascii(): Out of memory, can't log anything!\n"); + return NULL; + } + + d_ptr = ret_val; + + for(i=0;i 0x1F) && (xdata[i] < 0x7F)) + { + if(xdata[i] == '<') + { + SnortStrncpy(d_ptr, "<", size - (d_ptr - ret_val)); + d_ptr+=4; + } + else if(xdata[i] == '&') + { + SnortStrncpy(d_ptr, "&", size - (d_ptr - ret_val)); + d_ptr += 5; + } + else if(xdata[i] == '>') + { + SnortStrncpy(d_ptr, ">", size - (d_ptr - ret_val)); + d_ptr += 4; + } + else + { + *d_ptr++ = xdata[i]; + } + } + else + { + *d_ptr++ = '.'; + } + } + + *d_ptr++ = '\0'; + + return ret_val; +} + +/**************************************************************************** + * + * Function: hex(u_char *xdata, int length) + * + * Purpose: This function takes takes a buffer "xdata" and its length then + * returns a string of hex with no spaces + * + * Arguments: xdata is the buffer, length is the length of the buffer in + * bytes + * + * Returns: char * -- You must free this char * when you are done with it. + * + ***************************************************************************/ +char *hex(const u_char *xdata, int length) +{ + int x; + char *rval = NULL; + char *buf = NULL; + + if (xdata == NULL) + return NULL; + + buf = (char *)calloc((length * 2) + 1, sizeof(char)); + + if (buf != NULL) + { + rval = buf; + + for (x = 0; x < length; x++) + { + SnortSnprintf(buf, 3, "%02X", xdata[x]); + buf += 2; + } + + rval[length * 2] = '\0'; + } + + return rval; +} + + + +char *fasthex(const u_char *xdata, int length) +{ + char conv[] = "0123456789ABCDEF"; + char *retbuf = NULL; + const u_char *index; + const u_char *end; + char *ridx; + + index = xdata; + end = xdata + length; + retbuf = (char *)SnortAlloc(((length * 2) + 1) * sizeof(char)); + ridx = retbuf; + + while(index < end) + { + *ridx++ = conv[((*index & 0xFF)>>4)]; + *ridx++ = conv[((*index & 0xFF)&0x0F)]; + index++; + } + + return retbuf; +} + diff -uNr snort-2.8.3.1/src/plugin_enum.h snortsam-2.8.3.1/src/plugin_enum.h --- snort-2.8.3.1/src/plugin_enum.h 2008-02-25 13:27:42.000000000 -0600 +++ snortsam-2.8.3.1/src/plugin_enum.h 2008-11-05 19:10:36.000000000 -0600 @@ -60,6 +60,7 @@ PLUGIN_URILEN_CHECK, PLUGIN_DYNAMIC, PLUGIN_FLOWBIT, + PLUGIN_FWSAM, PLUGIN_MAX /* sentinel value */ }; diff -uNr snort-2.8.3.1/src/plugin_enum.h.orig snortsam-2.8.3.1/src/plugin_enum.h.orig --- snort-2.8.3.1/src/plugin_enum.h.orig 1969-12-31 18:00:00.000000000 -0600 +++ snortsam-2.8.3.1/src/plugin_enum.h.orig 2008-02-25 13:27:42.000000000 -0600 @@ -0,0 +1,67 @@ +/* $Id$ */ +/**************************************************************************** + * + * Copyright (C) 2003-2008 Sourcefire, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation. You may not use, modify or + * distribute this program under any other version of the GNU General + * Public License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ****************************************************************************/ + +/* + Purpose: Enumerate all the various detection plugins entries for + otn->ds_list[] + + No more grepping to make your own plugin! +*/ + +#ifndef _PLUGIN_ENUM_H +#define _PLUGIN_ENUM_H + +enum { + PLUGIN_CLIENTSERVER, + PLUGIN_DSIZE_CHECK, + PLUGIN_FRAG_BITS, + PLUGIN_FRAG_OFFSET, + PLUGIN_ICMP_CODE, + PLUGIN_ICMP_ID_CHECK, + PLUGIN_ICMP_SEQ_CHECK, + PLUGIN_ICMP_TYPE, + PLUGIN_IPOPTION_CHECK, + PLUGIN_IP_ID_CHECK, + PLUGIN_IP_PROTO_CHECK, + PLUGIN_IP_SAME_CHECK, + PLUGIN_IP_TOS_CHECK, + PLUGIN_PATTERN_MATCH, /* AND match */ + PLUGIN_PATTERN_MATCH_OR, + PLUGIN_PATTERN_MATCH_URI, + PLUGIN_RESPOND, + PLUGIN_RPC_CHECK, + PLUGIN_SESSION, + PLUGIN_TCP_ACK_CHECK, + PLUGIN_TCP_FLAG_CHECK, + PLUGIN_TCP_SEQ_CHECK, + PLUGIN_TCP_WIN_CHECK, + PLUGIN_TTL_CHECK, + PLUGIN_BYTE_TEST, + PLUGIN_PCRE, + PLUGIN_URILEN_CHECK, + PLUGIN_DYNAMIC, + PLUGIN_FLOWBIT, + PLUGIN_MAX /* sentinel value */ +}; + +#endif /* _PLUGIN_ENUM_H */ + diff -uNr snort-2.8.3.1/src/twofish.c snortsam-2.8.3.1/src/twofish.c --- snort-2.8.3.1/src/twofish.c 1969-12-31 18:00:00.000000000 -0600 +++ snortsam-2.8.3.1/src/twofish.c 2008-11-05 19:08:29.000000000 -0600 @@ -0,0 +1,946 @@ +/* $Id: snortpatchb,v 1.5 2005/10/06 08:50:39 fknobbe Exp $ + * + * + * Copyright (C) 1997-2000 The Cryptix Foundation Limited. + * Copyright (C) 2000 Farm9. + * Copyright (C) 2001 Frank Knobbe. + * All rights reserved. + * + * For Cryptix code: + * Use, modification, copying and distribution of this software is subject + * the terms and conditions of the Cryptix General Licence. You should have + * received a copy of the Cryptix General Licence along with this library; + * if not, you can download a copy from http://www.cryptix.org/ . + * + * For Farm9: + * --- jojo@farm9.com, August 2000, converted from Java to C++, added CBC mode and + * ciphertext stealing technique, added AsciiTwofish class for easy encryption + * decryption of text strings + * + * Frank Knobbe : + * --- April 2001, converted from C++ to C, prefixed global variables + * with TwoFish, substituted some defines, changed functions to make use of + * variables supplied in a struct, modified and added routines for modular calls. + * Cleaned up the code so that defines are used instead of fixed 16's and 32's. + * Created two general purpose crypt routines for one block and multiple block + * encryption using Joh's CBC code. + * Added crypt routines that use a header (with a magic and data length). + * (Basically a major rewrite). + * + * Note: Routines labeled _TwoFish are private and should not be used + * (or with extreme caution). + * + */ + +#ifndef __TWOFISH_LIBRARY_SOURCE__ +#define __TWOFISH_LIBRARY_SOURCE__ + +#include +#include +#include +#include +#include "twofish.h" + + +bool TwoFish_srand=TRUE; /* if TRUE, first call of TwoFishInit will seed rand(); */ + /* of TwoFishInit */ + +/* Fixed 8x8 permutation S-boxes */ +static const unsigned char TwoFish_P[2][256] = +{ + { /* p0 */ + 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, 0x9A, 0x92, 0x80, 0x78, + 0xE4, 0xDD, 0xD1, 0x38, 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, + 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, 0xF2, 0xD0, 0x8B, 0x30, + 0x84, 0x54, 0xDF, 0x23, 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, + 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, 0xA6, 0xEB, 0xA5, 0xBE, + 0x16, 0x0C, 0xE3, 0x61, 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, + 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, 0xE1, 0xE6, 0xBD, 0x45, + 0xE2, 0xF4, 0xB6, 0x66, 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, + 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, 0xEA, 0x77, 0x39, 0xAF, + 0x33, 0xC9, 0x62, 0x71, 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, + 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, 0xA1, 0x1D, 0xAA, 0xED, + 0x06, 0x70, 0xB2, 0xD2, 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, + 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, 0x9E, 0x9C, 0x52, 0x1B, + 0x5F, 0x93, 0x0A, 0xEF, 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, + 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, 0x2A, 0xCE, 0xCB, 0x2F, + 0xFC, 0x97, 0x05, 0x7A, 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, + 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, 0xB8, 0xDA, 0xB0, 0x17, + 0x55, 0x1F, 0x8A, 0x7D, 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, + 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, 0x6E, 0x50, 0xDE, 0x68, + 0x65, 0xBC, 0xDB, 0xF8, 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, + 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, 0x6F, 0x9D, 0x36, 0x42, + 0x4A, 0x5E, 0xC1, 0xE0 + }, + { /* p1 */ + 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, 0x4A, 0xD3, 0xE6, 0x6B, + 0x45, 0x7D, 0xE8, 0x4B, 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, + 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, 0x5E, 0xBA, 0xAE, 0x5B, + 0x8A, 0x00, 0xBC, 0x9D, 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, + 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, 0xB2, 0x73, 0x4C, 0x54, + 0x92, 0x74, 0x36, 0x51, 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, + 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, 0x13, 0x95, 0x9C, 0xC7, + 0x24, 0x46, 0x3B, 0x70, 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, + 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, 0x03, 0x6F, 0x08, 0xBF, + 0x40, 0xE7, 0x2B, 0xE2, 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, + 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, 0x66, 0x94, 0xA1, 0x1D, + 0x3D, 0xF0, 0xDE, 0xB3, 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, + 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, 0x81, 0x88, 0xEE, 0x21, + 0xC4, 0x1A, 0xEB, 0xD9, 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, + 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, 0x4F, 0xF2, 0x65, 0x8E, + 0x78, 0x5C, 0x58, 0x19, 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, + 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, 0xCE, 0xE9, 0x68, 0x44, + 0xE0, 0x4D, 0x43, 0x69, 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, + 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, 0x22, 0xC9, 0xC0, 0x9B, + 0x89, 0xD4, 0xED, 0xAB, 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, + 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, 0x16, 0x25, 0x86, 0x56, + 0x55, 0x09, 0xBE, 0x91 + } +}; + +static bool TwoFish_MDSready=FALSE; +static unsigned long TwoFish_MDS[4][256]; /* TwoFish_MDS matrix */ + + +#define TwoFish_LFSR1(x) (((x)>>1)^(((x)&0x01)?TwoFish_MDS_GF_FDBK/2:0)) +#define TwoFish_LFSR2(x) (((x)>>2)^(((x)&0x02)?TwoFish_MDS_GF_FDBK/2:0)^(((x)&0x01)?TwoFish_MDS_GF_FDBK/4:0)) + +#define TwoFish_Mx_1(x) ((unsigned long)(x)) /* force result to dword so << will work */ +#define TwoFish_Mx_X(x) ((unsigned long)((x)^TwoFish_LFSR2(x))) /* 5B */ +#define TwoFish_Mx_Y(x) ((unsigned long)((x)^TwoFish_LFSR1(x)^TwoFish_LFSR2(x))) /* EF */ +#define TwoFish_RS_rem(x) { unsigned char b=(unsigned char)(x>>24); unsigned long g2=((b<<1)^((b&0x80)?TwoFish_RS_GF_FDBK:0))&0xFF; unsigned long g3=((b>>1)&0x7F)^((b&1)?TwoFish_RS_GF_FDBK>>1:0)^g2; x=(x<<8)^(g3<<24)^(g2<<16)^(g3<<8)^b; } + +/*#define TwoFish__b(x,N) (((unsigned char *)&x)[((N)&3)^TwoFish_ADDR_XOR])*/ /* pick bytes out of a dword */ + +#define TwoFish_b0(x) TwoFish__b(x,0) /* extract LSB of unsigned long */ +#define TwoFish_b1(x) TwoFish__b(x,1) +#define TwoFish_b2(x) TwoFish__b(x,2) +#define TwoFish_b3(x) TwoFish__b(x,3) /* extract MSB of unsigned long */ + +unsigned char TwoFish__b(unsigned long x,int n) +{ n&=3; + while(n-->0) + x>>=8; + return (unsigned char)x; +} + + +/* TwoFish Initialization + * + * This routine generates a global data structure for use with TwoFish, + * initializes important values (such as subkeys, sBoxes), generates subkeys + * and precomputes the MDS matrix if not already done. + * + * Input: User supplied password (will be appended by default password of 'SnortHas2FishEncryptionRoutines!') + * + * Output: Pointer to TWOFISH structure. This data structure contains key dependent data. + * This pointer is used with all other crypt functions. + */ + +TWOFISH *TwoFishInit(char *userkey) +{ TWOFISH *tfdata; + int i,x,m; + char tkey[TwoFish_KEY_LENGTH+40]; + + tfdata=malloc(sizeof(TWOFISH)); /* allocate the TwoFish structure */ + if(tfdata!=NULL) + { if(*userkey) + { strncpy(tkey,userkey,TwoFish_KEY_LENGTH); /* use first 32 chars of user supplied password */ + tkey[TwoFish_KEY_LENGTH]=0; /* make sure it wasn't more */ + } + else + strcpy(tkey,TwoFish_DEFAULT_PW); /* if no key defined, use default password */ + for(i=0,x=0,m=strlen(tkey);ikey[i]=tkey[x++]; /* fill the whole keyspace with repeating key. */ + if(x==m) + x=0; + } + + if(!TwoFish_MDSready) + _TwoFish_PrecomputeMDSmatrix(); /* "Wake Up, Neo" */ + _TwoFish_MakeSubKeys(tfdata); /* generate subkeys */ + _TwoFish_ResetCBC(tfdata); /* reset the CBC */ + tfdata->output=NULL; /* nothing to output yet */ + tfdata->dontflush=FALSE; /* reset decrypt skip block flag */ + if(TwoFish_srand) + { TwoFish_srand=FALSE; + srand(time(NULL)); + } + } + return tfdata; /* return the data pointer */ +} + + +void TwoFishDestroy(TWOFISH *tfdata) +{ if(tfdata!=NULL) + free(tfdata); +} + + +/* en/decryption with CBC mode */ +unsigned long _TwoFish_CryptRawCBC(char *in,char *out,unsigned long len,bool decrypt,TWOFISH *tfdata) +{ unsigned long rl; + + rl=len; /* remember how much data to crypt. */ + while(len>TwoFish_BLOCK_SIZE) /* and now we process block by block. */ + { _TwoFish_BlockCrypt(in,out,TwoFish_BLOCK_SIZE,decrypt,tfdata); /* de/encrypt it. */ + in+=TwoFish_BLOCK_SIZE; /* adjust pointers. */ + out+=TwoFish_BLOCK_SIZE; + len-=TwoFish_BLOCK_SIZE; + } + if(len>0) /* if we have less than a block left... */ + _TwoFish_BlockCrypt(in,out,len,decrypt,tfdata); /* ...then we de/encrypt that too. */ + if(tfdata->qBlockDefined && !tfdata->dontflush) /* in case len was exactly one block... */ + _TwoFish_FlushOutput(tfdata->qBlockCrypt,TwoFish_BLOCK_SIZE,tfdata); /* ...we need to write the... */ + /* ...remaining bytes of the buffer */ + return rl; +} + +/* en/decryption on one block only */ +unsigned long _TwoFish_CryptRaw16(char *in,char *out,unsigned long len,bool decrypt,TWOFISH *tfdata) +{ /* qBlockPlain already zero'ed through ResetCBC */ + memcpy(tfdata->qBlockPlain,in,len); /* toss the data into it. */ + _TwoFish_BlockCrypt16(tfdata->qBlockPlain,tfdata->qBlockCrypt,decrypt,tfdata); /* encrypt just that block without CBC. */ + memcpy(out,tfdata->qBlockCrypt,TwoFish_BLOCK_SIZE); /* and return what we got */ + return TwoFish_BLOCK_SIZE; +} + +/* en/decryption without reset of CBC and output assignment */ +unsigned long _TwoFish_CryptRaw(char *in,char *out,unsigned long len,bool decrypt,TWOFISH *tfdata) +{ + if(in!=NULL && out!=NULL && len>0 && tfdata!=NULL) /* if we have valid data, then... */ + { if(len>TwoFish_BLOCK_SIZE) /* ...check if we have more than one block. */ + return _TwoFish_CryptRawCBC(in,out,len,decrypt,tfdata); /* if so, use the CBC routines... */ + else + return _TwoFish_CryptRaw16(in,out,len,decrypt,tfdata); /* ...otherwise just do one block. */ + } + return 0; +} + + +/* TwoFish Raw Encryption + * + * Does not use header, but does use CBC (if more than one block has to be encrypted). + * + * Input: Pointer to the buffer of the plaintext to be encrypted. + * Pointer to the buffer receiving the ciphertext. + * The length of the plaintext buffer. + * The TwoFish structure. + * + * Output: The amount of bytes encrypted if successful, otherwise 0. + */ + +unsigned long TwoFishEncryptRaw(char *in, + char *out, + unsigned long len, + TWOFISH *tfdata) +{ _TwoFish_ResetCBC(tfdata); /* reset CBC flag. */ + tfdata->output=out; /* output straight into output buffer. */ + return _TwoFish_CryptRaw(in,out,len,FALSE,tfdata); /* and go for it. */ +} + +/* TwoFish Raw Decryption + * + * Does not use header, but does use CBC (if more than one block has to be decrypted). + * + * Input: Pointer to the buffer of the ciphertext to be decrypted. + * Pointer to the buffer receiving the plaintext. + * The length of the ciphertext buffer (at least one cipher block). + * The TwoFish structure. + * + * Output: The amount of bytes decrypted if successful, otherwise 0. + */ + +unsigned long TwoFishDecryptRaw(char *in, + char *out, + unsigned long len, + TWOFISH *tfdata) +{ _TwoFish_ResetCBC(tfdata); /* reset CBC flag. */ + tfdata->output=out; /* output straight into output buffer. */ + return _TwoFish_CryptRaw(in,out,len,TRUE,tfdata); /* and go for it. */ +} + +/* TwoFish Free + * + * Free's the allocated buffer. + * + * Input: Pointer to the TwoFish structure + * + * Output: (none) + */ + +void TwoFishFree(TWOFISH *tfdata) +{ if(tfdata->output!=NULL) /* if a valid buffer is present... */ + { free(tfdata->output); /* ...then we free it for you... */ + tfdata->output=NULL; /* ...and mark as such. */ + } +} + +/* TwoFish Set Output + * + * If you want to allocate the output buffer yourself, + * then you can set it with this function. + * + * Input: Pointer to your output buffer + * Pointer to the TwoFish structure + * + * Output: (none) + */ + +void TwoFishSetOutput(char *outp,TWOFISH *tfdata) +{ tfdata->output=outp; /* (do we really need a function for this?) */ +} + +/* TwoFish Alloc + * + * Allocates enough memory for the output buffer that would be required + * + * Input: Length of the plaintext. + * Boolean flag for BinHex Output. + * Pointer to the TwoFish structure. + * + * Output: Returns a pointer to the memory allocated. + */ + +void *TwoFishAlloc(unsigned long len,bool binhex,bool decrypt,TWOFISH *tfdata) +{ +/* TwoFishFree(tfdata); */ /* (don't for now) discard whatever was allocated earlier. */ + if(decrypt) /* if decrypting... */ + { if(binhex) /* ...and input is binhex encoded... */ + len/=2; /* ...use half as much for output. */ + len-=TwoFish_BLOCK_SIZE; /* Also, subtract the size of the header. */ + } + else + { len+=TwoFish_BLOCK_SIZE; /* the size is just increased by the header... */ + if(binhex) + len*=2; /* ...and doubled if output is to be binhexed. */ + } + tfdata->output=malloc(len+TwoFish_BLOCK_SIZE);/* grab some memory...plus some extra (it's running over somewhere, crashes without extra padding) */ + + return tfdata->output; /* ...and return to caller. */ +} + +/* bin2hex and hex2bin conversion */ +void _TwoFish_BinHex(unsigned char *buf,unsigned long len,bool bintohex) +{ unsigned char *pi,*po,c; + + if(bintohex) + { for(pi=buf+len-1,po=buf+(2*len)-1;len>0;pi--,po--,len--) /* let's start from the end of the bin block. */ + { c=*pi; /* grab value. */ + c&=15; /* use lower 4 bits. */ + if(c>9) /* convert to ascii. */ + c+=('a'-10); + else + c+='0'; + *po--=c; /* set the lower nibble. */ + c=*pi; /* grab value again. */ + c>>=4; /* right shift 4 bits. */ + c&=15; /* make sure we only have 4 bits. */ + if(c>9) /* convert to ascii. */ + c+=('a'-10); + else + c+='0'; + *po=c; /* set the higher nibble. */ + } /* and keep going. */ + } + else + { for(pi=buf,po=buf;len>0;pi++,po++,len-=2) /* let's start from the beginning of the hex block. */ + { c=tolower(*pi++)-'0'; /* grab higher nibble. */ + if(c>9) /* convert to value. */ + c-=('0'-9); + *po=c<<4; /* left shit 4 bits. */ + c=tolower(*pi)-'0'; /* grab lower nibble. */ + if(c>9) /* convert to value. */ + c-=('0'-9); + *po|=c; /* and add to value. */ + } + } +} + + +/* TwoFish Encryption + * + * Uses header and CBC. If the output area has not been intialized with TwoFishAlloc, + * this routine will alloc the memory. In addition, it will include a small 'header' + * containing the magic and some salt. That way the decrypt routine can check if the + * packet got decrypted successfully, and return 0 instead of garbage. + * + * Input: Pointer to the buffer of the plaintext to be encrypted. + * Pointer to the pointer to the buffer receiving the ciphertext. + * The pointer either points to user allocated output buffer space, or to NULL, in which case + * this routine will set the pointer to the buffer allocated through the struct. + * The length of the plaintext buffer. + * Can be -1 if the input is a null terminated string, in which case we'll count for you. + * Boolean flag for BinHex Output (if used, output will be twice as large as input). + * Note: BinHex conversion overwrites (converts) input buffer! + * The TwoFish structure. + * + * Output: The amount of bytes encrypted if successful, otherwise 0. + */ + +unsigned long TwoFishEncrypt(char *in, + char **out, + signed long len, + bool binhex, + TWOFISH *tfdata) +{ unsigned long ilen,olen; + + + if(len== -1) /* if we got -1 for len, we'll assume IN is a... */ + ilen=strlen(in); /* ...\0 terminated string and figure len out ourselves... */ + else + ilen=len; /* ...otherwise we trust you supply a correct length. */ + + if(in!=NULL && out!=NULL && ilen>0 && tfdata!=NULL) /* if we got usable stuff, we'll do it. */ + { if(*out==NULL) /* if OUT points to a NULL pointer... */ + *out=TwoFishAlloc(ilen,binhex,FALSE,tfdata); /* ...we'll (re-)allocate buffer space. */ + if(*out!=NULL) + { tfdata->output=*out; /* set output buffer. */ + tfdata->header.salt=rand()*65536+rand(); /* toss in some salt. */ + tfdata->header.length[0]= (unsigned char)(ilen); + tfdata->header.length[1]= (unsigned char)(ilen>>8); + tfdata->header.length[2]= (unsigned char)(ilen>>16); + tfdata->header.length[3]= (unsigned char)(ilen>>24); + memcpy(tfdata->header.magic,TwoFish_MAGIC,TwoFish_MAGIC_LEN); /* set the magic. */ + olen=TwoFish_BLOCK_SIZE; /* set output counter. */ + _TwoFish_ResetCBC(tfdata); /* reset the CBC flag */ + _TwoFish_BlockCrypt((unsigned char *)&(tfdata->header),*out,olen,FALSE,tfdata); /* encrypt first block (without flush on 16 byte boundary). */ + olen+=_TwoFish_CryptRawCBC(in,*out+TwoFish_BLOCK_SIZE,ilen,FALSE,tfdata); /* and encrypt the rest (we do not reset the CBC flag). */ + if(binhex) /* if binhex... */ + { _TwoFish_BinHex(*out,olen,TRUE); /* ...convert output to binhex... */ + olen*=2; /* ...and size twice as large. */ + } + tfdata->output=*out; + return olen; + } + } + return 0; +} + +/* TwoFish Decryption + * + * Uses header and CBC. If the output area has not been intialized with TwoFishAlloc, + * this routine will alloc the memory. In addition, it will check the small 'header' + * containing the magic. If magic does not match we return 0. Otherwise we return the + * amount of bytes decrypted (should be the same as the length in the header). + * + * Input: Pointer to the buffer of the ciphertext to be decrypted. + * Pointer to the pointer to the buffer receiving the plaintext. + * The pointer either points to user allocated output buffer space, or to NULL, in which case + * this routine will set the pointer to the buffer allocated through the struct. + * The length of the ciphertext buffer. + * Can be -1 if the input is a null terminated binhex string, in which case we'll count for you. + * Boolean flag for BinHex Input (if used, plaintext will be half as large as input). + * Note: BinHex conversion overwrites (converts) input buffer! + * The TwoFish structure. + * + * Output: The amount of bytes decrypted if successful, otherwise 0. + */ + +unsigned long TwoFishDecrypt(char *in, + char **out, + signed long len, + bool binhex, + TWOFISH *tfdata) +{ unsigned long ilen,elen,olen; + const unsigned char cmagic[TwoFish_MAGIC_LEN]=TwoFish_MAGIC; + unsigned char *tbuf; + + + + if(len== -1) /* if we got -1 for len, we'll assume IN is... */ + ilen=strlen(in); /* ...\0 terminated binhex and figure len out ourselves... */ + else + ilen=len; /* ...otherwise we trust you supply a correct length. */ + + if(in!=NULL && out!=NULL && ilen>0 && tfdata!=NULL) /* if we got usable stuff, we'll do it. */ + { if(*out==NULL) /* if OUT points to a NULL pointer... */ + *out=TwoFishAlloc(ilen,binhex,TRUE,tfdata); /* ...we'll (re-)allocate buffer space. */ + if(*out!=NULL) + { if(binhex) /* if binhex... */ + { _TwoFish_BinHex(in,ilen,FALSE); /* ...convert input to values... */ + ilen/=2; /* ...and size half as much. */ + } + _TwoFish_ResetCBC(tfdata); /* reset the CBC flag. */ + + tbuf=(unsigned char *)malloc(ilen+TwoFish_BLOCK_SIZE); /* get memory for data and header. */ + if(tbuf==NULL) + return 0; + tfdata->output=tbuf; /* set output to temp buffer. */ + + olen=_TwoFish_CryptRawCBC(in,tbuf,ilen,TRUE,tfdata)-TwoFish_BLOCK_SIZE; /* decrypt the whole thing. */ + memcpy(&(tfdata->header),tbuf,TwoFish_BLOCK_SIZE); /* copy first block into header. */ + tfdata->output=*out; + for(elen=0;elenheader.magic[elen]!=cmagic[elen]) + break; + if(elen==TwoFish_MAGIC_LEN) /* if magic matches then... */ + { elen=(tfdata->header.length[0]) | + (tfdata->header.length[1])<<8 | + (tfdata->header.length[2])<<16 | + (tfdata->header.length[3])<<24; /* .. we know how much to expect. */ + if(elen>olen) /* adjust if necessary. */ + elen=olen; + memcpy(*out,tbuf+TwoFish_BLOCK_SIZE,elen); /* copy data into intended output. */ + free(tbuf); + return elen; + } + free(tbuf); + } + } + return 0; +} + +void _TwoFish_PrecomputeMDSmatrix(void) /* precompute the TwoFish_MDS matrix */ +{ unsigned long m1[2]; + unsigned long mX[2]; + unsigned long mY[2]; + unsigned long i, j; + + for (i = 0; i < 256; i++) + { j = TwoFish_P[0][i] & 0xFF; /* compute all the matrix elements */ + m1[0] = j; + mX[0] = TwoFish_Mx_X( j ) & 0xFF; + mY[0] = TwoFish_Mx_Y( j ) & 0xFF; + + j = TwoFish_P[1][i] & 0xFF; + m1[1] = j; + mX[1] = TwoFish_Mx_X( j ) & 0xFF; + mY[1] = TwoFish_Mx_Y( j ) & 0xFF; + + TwoFish_MDS[0][i] = m1[TwoFish_P_00] | /* fill matrix w/ above elements */ + mX[TwoFish_P_00] << 8 | + mY[TwoFish_P_00] << 16 | + mY[TwoFish_P_00] << 24; + TwoFish_MDS[1][i] = mY[TwoFish_P_10] | + mY[TwoFish_P_10] << 8 | + mX[TwoFish_P_10] << 16 | + m1[TwoFish_P_10] << 24; + TwoFish_MDS[2][i] = mX[TwoFish_P_20] | + mY[TwoFish_P_20] << 8 | + m1[TwoFish_P_20] << 16 | + mY[TwoFish_P_20] << 24; + TwoFish_MDS[3][i] = mX[TwoFish_P_30] | + m1[TwoFish_P_30] << 8 | + mY[TwoFish_P_30] << 16 | + mX[TwoFish_P_30] << 24; + } + TwoFish_MDSready=TRUE; +} + + +void _TwoFish_MakeSubKeys(TWOFISH *tfdata) /* Expand a user-supplied key material into a session key. */ +{ unsigned long k64Cnt = TwoFish_KEY_LENGTH / 8; + unsigned long k32e[4]; /* even 32-bit entities */ + unsigned long k32o[4]; /* odd 32-bit entities */ + unsigned long sBoxKey[4]; + unsigned long offset,i,j; + unsigned long A, B, q=0; + unsigned long k0,k1,k2,k3; + unsigned long b0,b1,b2,b3; + + /* split user key material into even and odd 32-bit entities and */ + /* compute S-box keys using (12, 8) Reed-Solomon code over GF(256) */ + + + for (offset=0,i=0,j=k64Cnt-1;i<4 && offsetkey[offset++]; + k32e[i]|= tfdata->key[offset++]<<8; + k32e[i]|= tfdata->key[offset++]<<16; + k32e[i]|= tfdata->key[offset++]<<24; + k32o[i] = tfdata->key[offset++]; + k32o[i]|= tfdata->key[offset++]<<8; + k32o[i]|= tfdata->key[offset++]<<16; + k32o[i]|= tfdata->key[offset++]<<24; + sBoxKey[j] = _TwoFish_RS_MDS_Encode( k32e[i], k32o[i] ); /* reverse order */ + } + + /* compute the round decryption subkeys for PHT. these same subkeys */ + /* will be used in encryption but will be applied in reverse order. */ + i=0; + while(i < TwoFish_TOTAL_SUBKEYS) + { A = _TwoFish_F32( k64Cnt, q, k32e ); /* A uses even key entities */ + q += TwoFish_SK_BUMP; + + B = _TwoFish_F32( k64Cnt, q, k32o ); /* B uses odd key entities */ + q += TwoFish_SK_BUMP; + + B = B << 8 | B >> 24; + + A += B; + tfdata->subKeys[i++] = A; /* combine with a PHT */ + + A += B; + tfdata->subKeys[i++] = A << TwoFish_SK_ROTL | A >> (32-TwoFish_SK_ROTL); + } + + /* fully expand the table for speed */ + k0 = sBoxKey[0]; + k1 = sBoxKey[1]; + k2 = sBoxKey[2]; + k3 = sBoxKey[3]; + + for (i = 0; i < 256; i++) + { b0 = b1 = b2 = b3 = i; + switch (k64Cnt & 3) + { case 1: /* 64-bit keys */ + tfdata->sBox[ 2*i ] = TwoFish_MDS[0][(TwoFish_P[TwoFish_P_01][b0]) ^ TwoFish_b0(k0)]; + tfdata->sBox[ 2*i+1] = TwoFish_MDS[1][(TwoFish_P[TwoFish_P_11][b1]) ^ TwoFish_b1(k0)]; + tfdata->sBox[0x200+2*i ] = TwoFish_MDS[2][(TwoFish_P[TwoFish_P_21][b2]) ^ TwoFish_b2(k0)]; + tfdata->sBox[0x200+2*i+1] = TwoFish_MDS[3][(TwoFish_P[TwoFish_P_31][b3]) ^ TwoFish_b3(k0)]; + break; + case 0: /* 256-bit keys (same as 4) */ + b0 = (TwoFish_P[TwoFish_P_04][b0]) ^ TwoFish_b0(k3); + b1 = (TwoFish_P[TwoFish_P_14][b1]) ^ TwoFish_b1(k3); + b2 = (TwoFish_P[TwoFish_P_24][b2]) ^ TwoFish_b2(k3); + b3 = (TwoFish_P[TwoFish_P_34][b3]) ^ TwoFish_b3(k3); + case 3: /* 192-bit keys */ + b0 = (TwoFish_P[TwoFish_P_03][b0]) ^ TwoFish_b0(k2); + b1 = (TwoFish_P[TwoFish_P_13][b1]) ^ TwoFish_b1(k2); + b2 = (TwoFish_P[TwoFish_P_23][b2]) ^ TwoFish_b2(k2); + b3 = (TwoFish_P[TwoFish_P_33][b3]) ^ TwoFish_b3(k2); + case 2: /* 128-bit keys */ + tfdata->sBox[ 2*i ]= + TwoFish_MDS[0][(TwoFish_P[TwoFish_P_01][(TwoFish_P[TwoFish_P_02][b0]) ^ + TwoFish_b0(k1)]) ^ TwoFish_b0(k0)]; + + tfdata->sBox[ 2*i+1]= + TwoFish_MDS[1][(TwoFish_P[TwoFish_P_11][(TwoFish_P[TwoFish_P_12][b1]) ^ + TwoFish_b1(k1)]) ^ TwoFish_b1(k0)]; + + tfdata->sBox[0x200+2*i ]= + TwoFish_MDS[2][(TwoFish_P[TwoFish_P_21][(TwoFish_P[TwoFish_P_22][b2]) ^ + TwoFish_b2(k1)]) ^ TwoFish_b2(k0)]; + + tfdata->sBox[0x200+2*i+1]= + TwoFish_MDS[3][(TwoFish_P[TwoFish_P_31][(TwoFish_P[TwoFish_P_32][b3]) ^ + TwoFish_b3(k1)]) ^ TwoFish_b3(k0)]; + } + } +} + + +/** + * Encrypt or decrypt exactly one block of plaintext in CBC mode. + * Use "ciphertext stealing" technique described on pg. 196 + * of "Applied Cryptography" to encrypt the final partial + * (i.e. <16 byte) block if necessary. + * + * jojo: the "ciphertext stealing" requires we read ahead and have + * special handling for the last two blocks. Because of this, the + * output from the TwoFish algorithm is handled internally here. + * It would be better to have a higher level handle this as well as + * CBC mode. Unfortunately, I've mixed the two together, which is + * pretty crappy... The Java version separates these out correctly. + * + * fknobbe: I have reduced the CBC mode to work on memory buffer only. + * Higher routines should use an intermediate buffer and handle + * their output seperately (mainly so the data can be flushed + * in one chunk, not seperate 16 byte blocks...) + * + * @param in The plaintext. + * @param out The ciphertext + * @param size how much to encrypt + * @param tfdata: Pointer to the global data structure containing session keys. + * @return none + */ +void _TwoFish_BlockCrypt(unsigned char *in,unsigned char *out,unsigned long size,int decrypt,TWOFISH *tfdata) +{ unsigned char PnMinusOne[TwoFish_BLOCK_SIZE]; + unsigned char CnMinusOne[TwoFish_BLOCK_SIZE]; + unsigned char CBCplusCprime[TwoFish_BLOCK_SIZE]; + unsigned char Pn[TwoFish_BLOCK_SIZE]; + unsigned char *p,*pout; + unsigned long i; + + /* here is where we implement CBC mode and cipher block stealing */ + if(size==TwoFish_BLOCK_SIZE) + { /* if we are encrypting, CBC means we XOR the plain text block with the */ + /* previous cipher text block before encrypting */ + if(!decrypt && tfdata->qBlockDefined) + { for(p=in,i=0;iqBlockCrypt[i]; /* FK: I'm copying the xor'ed input into Pn... */ + } + else + memcpy(Pn,in,TwoFish_BLOCK_SIZE); /* FK: same here. we work of Pn all the time. */ + + /* TwoFish block level encryption or decryption */ + _TwoFish_BlockCrypt16(Pn,out,decrypt,tfdata); + + /* if we are decrypting, CBC means we XOR the result of the decryption */ + /* with the previous cipher text block to get the resulting plain text */ + if(decrypt && tfdata->qBlockDefined) + { for (p=out,i=0;iqBlockPlain[i]; + } + + /* save the input and output blocks, since CBC needs these for XOR */ + /* operations */ + _TwoFish_qBlockPush(Pn,out,tfdata); + } + else + { /* cipher block stealing, we are at Pn, */ + /* but since Cn-1 must now be replaced with CnC' */ + /* we pop it off, and recalculate Cn-1 */ + + if(decrypt) + { /* We are on an odd block, and had to do cipher block stealing, */ + /* so the PnMinusOne has to be derived differently. */ + + /* First we decrypt it into CBC and C' */ + _TwoFish_qBlockPop(CnMinusOne,PnMinusOne,tfdata); + _TwoFish_BlockCrypt16(CnMinusOne,CBCplusCprime,decrypt,tfdata); + + /* we then xor the first few bytes with the "in" bytes (Cn) */ + /* to recover Pn, which we put in out */ + for(p=in,pout=out,i=0;iprevCipher[i]; + + /* So at this point, out has PnMinusOne */ + _TwoFish_qBlockPush(CnMinusOne,PnMinusOne,tfdata); + _TwoFish_FlushOutput(tfdata->qBlockCrypt,TwoFish_BLOCK_SIZE,tfdata); + _TwoFish_FlushOutput(out,size,tfdata); + } + else + { _TwoFish_qBlockPop(PnMinusOne,CnMinusOne,tfdata); + memset(Pn,0,TwoFish_BLOCK_SIZE); + memcpy(Pn,in,size); + for(i=0;iqBlockCrypt,TwoFish_BLOCK_SIZE,tfdata); + _TwoFish_FlushOutput(CnMinusOne,size,tfdata); /* old Cn-1 becomes new partial Cn */ + } + tfdata->qBlockDefined=FALSE; + } +} + +void _TwoFish_qBlockPush(unsigned char *p,unsigned char *c,TWOFISH *tfdata) +{ if(tfdata->qBlockDefined) + _TwoFish_FlushOutput(tfdata->qBlockCrypt,TwoFish_BLOCK_SIZE,tfdata); + memcpy(tfdata->prevCipher,tfdata->qBlockPlain,TwoFish_BLOCK_SIZE); + memcpy(tfdata->qBlockPlain,p,TwoFish_BLOCK_SIZE); + memcpy(tfdata->qBlockCrypt,c,TwoFish_BLOCK_SIZE); + tfdata->qBlockDefined=TRUE; +} + +void _TwoFish_qBlockPop(unsigned char *p,unsigned char *c,TWOFISH *tfdata) +{ memcpy(p,tfdata->qBlockPlain,TwoFish_BLOCK_SIZE ); + memcpy(c,tfdata->qBlockCrypt,TwoFish_BLOCK_SIZE ); + tfdata->qBlockDefined=FALSE; +} + +/* Reset's the CBC flag and zero's PrevCipher (through qBlockPlain) (important) */ +void _TwoFish_ResetCBC(TWOFISH *tfdata) +{ tfdata->qBlockDefined=FALSE; + memset(tfdata->qBlockPlain,0,TwoFish_BLOCK_SIZE); +} + +void _TwoFish_FlushOutput(unsigned char *b,unsigned long len,TWOFISH *tfdata) +{ unsigned long i; + + for(i=0;idontflush;i++) + *tfdata->output++ = *b++; + tfdata->dontflush=FALSE; +} + +void _TwoFish_BlockCrypt16(unsigned char *in,unsigned char *out,bool decrypt,TWOFISH *tfdata) +{ unsigned long x0,x1,x2,x3; + unsigned long k,t0,t1,R; + + + x0=*in++; + x0|=(*in++ << 8 ); + x0|=(*in++ << 16); + x0|=(*in++ << 24); + x1=*in++; + x1|=(*in++ << 8 ); + x1|=(*in++ << 16); + x1|=(*in++ << 24); + x2=*in++; + x2|=(*in++ << 8 ); + x2|=(*in++ << 16); + x2|=(*in++ << 24); + x3=*in++; + x3|=(*in++ << 8 ); + x3|=(*in++ << 16); + x3|=(*in++ << 24); + + if(decrypt) + { x0 ^= tfdata->subKeys[4]; /* swap input and output whitening keys when decrypting */ + x1 ^= tfdata->subKeys[5]; + x2 ^= tfdata->subKeys[6]; + x3 ^= tfdata->subKeys[7]; + + k = 7+(TwoFish_ROUNDS*2); + for (R = 0; R < TwoFish_ROUNDS; R += 2) + { t0 = _TwoFish_Fe320( tfdata->sBox, x0); + t1 = _TwoFish_Fe323( tfdata->sBox, x1); + x3 ^= t0 + (t1<<1) + tfdata->subKeys[k--]; + x3 = x3 >> 1 | x3 << 31; + x2 = x2 << 1 | x2 >> 31; + x2 ^= t0 + t1 + tfdata->subKeys[k--]; + + t0 = _TwoFish_Fe320( tfdata->sBox, x2); + t1 = _TwoFish_Fe323( tfdata->sBox, x3); + x1 ^= t0 + (t1<<1) + tfdata->subKeys[k--]; + x1 = x1 >> 1 | x1 << 31; + x0 = x0 << 1 | x0 >> 31; + x0 ^= t0 + t1 + tfdata->subKeys[k--]; + } + + x2 ^= tfdata->subKeys[0]; + x3 ^= tfdata->subKeys[1]; + x0 ^= tfdata->subKeys[2]; + x1 ^= tfdata->subKeys[3]; + } + else + { x0 ^= tfdata->subKeys[0]; + x1 ^= tfdata->subKeys[1]; + x2 ^= tfdata->subKeys[2]; + x3 ^= tfdata->subKeys[3]; + + k = 8; + for (R = 0; R < TwoFish_ROUNDS; R += 2) + { t0 = _TwoFish_Fe320( tfdata->sBox, x0); + t1 = _TwoFish_Fe323( tfdata->sBox, x1); + x2 ^= t0 + t1 + tfdata->subKeys[k++]; + x2 = x2 >> 1 | x2 << 31; + x3 = x3 << 1 | x3 >> 31; + x3 ^= t0 + (t1<<1) + tfdata->subKeys[k++]; + + t0 = _TwoFish_Fe320( tfdata->sBox, x2); + t1 = _TwoFish_Fe323( tfdata->sBox, x3); + x0 ^= t0 + t1 + tfdata->subKeys[k++]; + x0 = x0 >> 1 | x0 << 31; + x1 = x1 << 1 | x1 >> 31; + x1 ^= t0 + (t1<<1) + tfdata->subKeys[k++]; + } + + x2 ^= tfdata->subKeys[4]; + x3 ^= tfdata->subKeys[5]; + x0 ^= tfdata->subKeys[6]; + x1 ^= tfdata->subKeys[7]; + } + + *out++ = (unsigned char)(x2 ); + *out++ = (unsigned char)(x2 >> 8); + *out++ = (unsigned char)(x2 >> 16); + *out++ = (unsigned char)(x2 >> 24); + + *out++ = (unsigned char)(x3 ); + *out++ = (unsigned char)(x3 >> 8); + *out++ = (unsigned char)(x3 >> 16); + *out++ = (unsigned char)(x3 >> 24); + + *out++ = (unsigned char)(x0 ); + *out++ = (unsigned char)(x0 >> 8); + *out++ = (unsigned char)(x0 >> 16); + *out++ = (unsigned char)(x0 >> 24); + + *out++ = (unsigned char)(x1 ); + *out++ = (unsigned char)(x1 >> 8); + *out++ = (unsigned char)(x1 >> 16); + *out++ = (unsigned char)(x1 >> 24); +} + +/** + * Use (12, 8) Reed-Solomon code over GF(256) to produce a key S-box + * 32-bit entity from two key material 32-bit entities. + * + * @param k0 1st 32-bit entity. + * @param k1 2nd 32-bit entity. + * @return Remainder polynomial generated using RS code + */ +unsigned long _TwoFish_RS_MDS_Encode(unsigned long k0,unsigned long k1) +{ unsigned long i,r; + + for(r=k1,i=0;i<4;i++) /* shift 1 byte at a time */ + TwoFish_RS_rem(r); + r ^= k0; + for(i=0;i<4;i++) + TwoFish_RS_rem(r); + + return r; +} + +unsigned long _TwoFish_F32(unsigned long k64Cnt,unsigned long x,unsigned long *k32) +{ unsigned char b0,b1,b2,b3; + unsigned long k0,k1,k2,k3,result = 0; + + b0=TwoFish_b0(x); + b1=TwoFish_b1(x); + b2=TwoFish_b2(x); + b3=TwoFish_b3(x); + k0=k32[0]; + k1=k32[1]; + k2=k32[2]; + k3=k32[3]; + + switch (k64Cnt & 3) + { case 1: /* 64-bit keys */ + result = + TwoFish_MDS[0][(TwoFish_P[TwoFish_P_01][b0] & 0xFF) ^ TwoFish_b0(k0)] ^ + TwoFish_MDS[1][(TwoFish_P[TwoFish_P_11][b1] & 0xFF) ^ TwoFish_b1(k0)] ^ + TwoFish_MDS[2][(TwoFish_P[TwoFish_P_21][b2] & 0xFF) ^ TwoFish_b2(k0)] ^ + TwoFish_MDS[3][(TwoFish_P[TwoFish_P_31][b3] & 0xFF) ^ TwoFish_b3(k0)]; + break; + case 0: /* 256-bit keys (same as 4) */ + b0 = (TwoFish_P[TwoFish_P_04][b0] & 0xFF) ^ TwoFish_b0(k3); + b1 = (TwoFish_P[TwoFish_P_14][b1] & 0xFF) ^ TwoFish_b1(k3); + b2 = (TwoFish_P[TwoFish_P_24][b2] & 0xFF) ^ TwoFish_b2(k3); + b3 = (TwoFish_P[TwoFish_P_34][b3] & 0xFF) ^ TwoFish_b3(k3); + + case 3: /* 192-bit keys */ + b0 = (TwoFish_P[TwoFish_P_03][b0] & 0xFF) ^ TwoFish_b0(k2); + b1 = (TwoFish_P[TwoFish_P_13][b1] & 0xFF) ^ TwoFish_b1(k2); + b2 = (TwoFish_P[TwoFish_P_23][b2] & 0xFF) ^ TwoFish_b2(k2); + b3 = (TwoFish_P[TwoFish_P_33][b3] & 0xFF) ^ TwoFish_b3(k2); + case 2: /* 128-bit keys (optimize for this case) */ + result = + TwoFish_MDS[0][(TwoFish_P[TwoFish_P_01][(TwoFish_P[TwoFish_P_02][b0] & 0xFF) ^ TwoFish_b0(k1)] & 0xFF) ^ TwoFish_b0(k0)] ^ + TwoFish_MDS[1][(TwoFish_P[TwoFish_P_11][(TwoFish_P[TwoFish_P_12][b1] & 0xFF) ^ TwoFish_b1(k1)] & 0xFF) ^ TwoFish_b1(k0)] ^ + TwoFish_MDS[2][(TwoFish_P[TwoFish_P_21][(TwoFish_P[TwoFish_P_22][b2] & 0xFF) ^ TwoFish_b2(k1)] & 0xFF) ^ TwoFish_b2(k0)] ^ + TwoFish_MDS[3][(TwoFish_P[TwoFish_P_31][(TwoFish_P[TwoFish_P_32][b3] & 0xFF) ^ TwoFish_b3(k1)] & 0xFF) ^ TwoFish_b3(k0)]; + break; + } + return result; +} + +unsigned long _TwoFish_Fe320(unsigned long *lsBox,unsigned long x) +{ return lsBox[ TwoFish_b0(x)<<1 ]^ + lsBox[ ((TwoFish_b1(x)<<1)|1)]^ + lsBox[0x200+ (TwoFish_b2(x)<<1) ]^ + lsBox[0x200+((TwoFish_b3(x)<<1)|1)]; +} + +unsigned long _TwoFish_Fe323(unsigned long *lsBox,unsigned long x) +{ return lsBox[ (TwoFish_b3(x)<<1) ]^ + lsBox[ ((TwoFish_b0(x)<<1)|1)]^ + lsBox[0x200+ (TwoFish_b1(x)<<1) ]^ + lsBox[0x200+((TwoFish_b2(x)<<1)|1)]; +} + +unsigned long _TwoFish_Fe32(unsigned long *lsBox,unsigned long x,unsigned long R) +{ return lsBox[ 2*TwoFish__b(x,R ) ]^ + lsBox[ 2*TwoFish__b(x,R+1)+1]^ + lsBox[0x200+2*TwoFish__b(x,R+2) ]^ + lsBox[0x200+2*TwoFish__b(x,R+3)+1]; +} + + +#endif diff -uNr snort-2.8.3.1/src/twofish.h snortsam-2.8.3.1/src/twofish.h --- snort-2.8.3.1/src/twofish.h 1969-12-31 18:00:00.000000000 -0600 +++ snortsam-2.8.3.1/src/twofish.h 2008-11-05 19:08:29.000000000 -0600 @@ -0,0 +1,276 @@ +/* $Id: snortpatchb,v 1.5 2005/10/06 08:50:39 fknobbe Exp $ + * + * + * Copyright (C) 1997-2000 The Cryptix Foundation Limited. + * Copyright (C) 2000 Farm9. + * Copyright (C) 2001 Frank Knobbe. + * All rights reserved. + * + * For Cryptix code: + * Use, modification, copying and distribution of this software is subject + * the terms and conditions of the Cryptix General Licence. You should have + * received a copy of the Cryptix General Licence along with this library; + * if not, you can download a copy from http://www.cryptix.org/ . + * + * For Farm9: + * --- jojo@farm9.com, August 2000, converted from Java to C++, added CBC mode and + * ciphertext stealing technique, added AsciiTwofish class for easy encryption + * decryption of text strings + * + * Frank Knobbe : + * --- April 2001, converted from C++ to C, prefixed global variables + * with TwoFish, substituted some defines, changed functions to make use of + * variables supplied in a struct, modified and added routines for modular calls. + * Cleaned up the code so that defines are used instead of fixed 16's and 32's. + * Created two general purpose crypt routines for one block and multiple block + * encryption using Joh's CBC code. + * Added crypt routines that use a header (with a magic and data length). + * (Basically a major rewrite). + * + * Note: Routines labeled _TwoFish are private and should not be used + * (or with extreme caution). + * + */ + +#ifndef __TWOFISH_LIBRARY_HEADER__ +#define __TWOFISH_LIBRARY_HEADER__ + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE !FALSE +#endif +#ifndef bool +#define bool int +#endif + + +/* Constants */ + +#define TwoFish_DEFAULT_PW "SnortHas2FishEncryptionRoutines!" /* default password (not more than 32 chars) */ +#define TwoFish_MAGIC "TwoFish" /* to indentify a successful decryption */ + +enum +{ TwoFish_KEY_SIZE = 256, /* Valid values: 64, 128, 192, 256 */ + /* User 256, other key sizes have not been tested. */ + /* (But should work. I substitutes as much as */ + /* I could with this define.) */ + TwoFish_ROUNDS = 16, + TwoFish_BLOCK_SIZE = 16, /* bytes in a data-block */ + TwoFish_KEY_LENGTH = TwoFish_KEY_SIZE/8, /* 32= 256-bit key */ + TwoFish_TOTAL_SUBKEYS = 4+4+2*TwoFish_ROUNDS, + TwoFish_MAGIC_LEN = TwoFish_BLOCK_SIZE-8, + TwoFish_SK_BUMP = 0x01010101, + TwoFish_SK_ROTL = 9, + TwoFish_P_00 = 1, + TwoFish_P_01 = 0, + TwoFish_P_02 = 0, + TwoFish_P_03 = TwoFish_P_01 ^ 1, + TwoFish_P_04 = 1, + TwoFish_P_10 = 0, + TwoFish_P_11 = 0, + TwoFish_P_12 = 1, + TwoFish_P_13 = TwoFish_P_11 ^ 1, + TwoFish_P_14 = 0, + TwoFish_P_20 = 1, + TwoFish_P_21 = 1, + TwoFish_P_22 = 0, + TwoFish_P_23 = TwoFish_P_21 ^ 1, + TwoFish_P_24 = 0, + TwoFish_P_30 = 0, + TwoFish_P_31 = 1, + TwoFish_P_32 = 1, + TwoFish_P_33 = TwoFish_P_31 ^ 1, + TwoFish_P_34 = 1, + TwoFish_GF256_FDBK = 0x169, + TwoFish_GF256_FDBK_2 = 0x169 / 2, + TwoFish_GF256_FDBK_4 = 0x169 / 4, + TwoFish_RS_GF_FDBK = 0x14D, /* field generator */ + TwoFish_MDS_GF_FDBK = 0x169 /* primitive polynomial for GF(256) */ +}; + + +/* Global data structure for callers */ + +typedef struct +{ unsigned long sBox[4 * 256]; /* Key dependent S-box */ + unsigned long subKeys[TwoFish_TOTAL_SUBKEYS]; /* Subkeys */ + unsigned char key[TwoFish_KEY_LENGTH]; /* Encryption Key */ + unsigned char *output; /* Pointer to output buffer */ + unsigned char qBlockPlain[TwoFish_BLOCK_SIZE]; /* Used by CBC */ + unsigned char qBlockCrypt[TwoFish_BLOCK_SIZE]; + unsigned char prevCipher[TwoFish_BLOCK_SIZE]; + struct /* Header for crypt functions. Has to be at least one block long. */ + { unsigned long salt; /* Random salt in first block (will salt the rest through CBC) */ + unsigned char length[4]; /* The amount of data following the header */ + unsigned char magic[TwoFish_MAGIC_LEN]; /* Magic to identify successful decryption */ + } header; + bool qBlockDefined; + bool dontflush; +} TWOFISH; + +#ifndef __TWOFISH_LIBRARY_SOURCE__ + +extern bool TwoFish_srand; /* if set to TRUE (default), first call of TwoFishInit will seed rand(); */ + /* call of TwoFishInit */ +#endif + + +/**** Public Functions ****/ + +/* TwoFish Initialization + * + * This routine generates a global data structure for use with TwoFish, + * initializes important values (such as subkeys, sBoxes), generates subkeys + * and precomputes the MDS matrix if not already done. + * + * Input: User supplied password (will be appended by default password of 'SnortHas2FishEncryptionRoutines!') + * + * Output: Pointer to TWOFISH structure. This data structure contains key dependent data. + * This pointer is used with all other crypt functions. + */ +TWOFISH *TwoFishInit(char *userkey); + + +/* TwoFish Destroy + * + * Nothing else but a free... + * + * Input: Pointer to the TwoFish structure. + * + */ +void TwoFishDestroy(TWOFISH *tfdata); + + +/* TwoFish Alloc + * + * Allocates enough memory for the output buffer as required. + * + * Input: Length of the plaintext. + * Boolean flag for BinHex Output. + * Pointer to the TwoFish structure. + * + * Output: Returns a pointer to the memory allocated. + */ +void *TwoFishAlloc(unsigned long len,bool binhex,bool decrypt,TWOFISH *tfdata); + + +/* TwoFish Free + * + * Free's the allocated buffer. + * + * Input: Pointer to the TwoFish structure + * + * Output: (none) + */ +void TwoFishFree(TWOFISH *tfdata); + + +/* TwoFish Set Output + * + * If you want to allocate the output buffer yourself, + * then you can set it with this function. + * + * Input: Pointer to your output buffer + * Pointer to the TwoFish structure + * + * Output: (none) + */ +void TwoFishSetOutput(char *outp,TWOFISH *tfdata); + + +/* TwoFish Raw Encryption + * + * Does not use header, but does use CBC (if more than one block has to be encrypted). + * + * Input: Pointer to the buffer of the plaintext to be encrypted. + * Pointer to the buffer receiving the ciphertext. + * The length of the plaintext buffer. + * The TwoFish structure. + * + * Output: The amount of bytes encrypted if successful, otherwise 0. + */ +unsigned long TwoFishEncryptRaw(char *in,char *out,unsigned long len,TWOFISH *tfdata); + +/* TwoFish Raw Decryption + * + * Does not use header, but does use CBC (if more than one block has to be decrypted). + * + * Input: Pointer to the buffer of the ciphertext to be decrypted. + * Pointer to the buffer receiving the plaintext. + * The length of the ciphertext buffer (at least one cipher block). + * The TwoFish structure. + * + * Output: The amount of bytes decrypted if successful, otherwise 0. + */ +unsigned long TwoFishDecryptRaw(char *in,char *out,unsigned long len,TWOFISH *tfdata); + + +/* TwoFish Encryption + * + * Uses header and CBC. If the output area has not been intialized with TwoFishAlloc, + * this routine will alloc the memory. In addition, it will include a small 'header' + * containing the magic and some salt. That way the decrypt routine can check if the + * packet got decrypted successfully, and return 0 instead of garbage. + * + * Input: Pointer to the buffer of the plaintext to be encrypted. + * Pointer to the pointer to the buffer receiving the ciphertext. + * The pointer either points to user allocated output buffer space, or to NULL, in which case + * this routine will set the pointer to the buffer allocated through the struct. + * The length of the plaintext buffer. + * Can be -1 if the input is a null terminated string, in which case we'll count for you. + * Boolean flag for BinHex Output (if used, output will be twice as large as input). + * Note: BinHex conversion overwrites (converts) input buffer! + * The TwoFish structure. + * + * Output: The amount of bytes encrypted if successful, otherwise 0. + */ +unsigned long TwoFishEncrypt(char *in,char **out,signed long len,bool binhex,TWOFISH *tfdata); + + +/* TwoFish Decryption + * + * Uses header and CBC. If the output area has not been intialized with TwoFishAlloc, + * this routine will alloc the memory. In addition, it will check the small 'header' + * containing the magic. If magic does not match we return 0. Otherwise we return the + * amount of bytes decrypted (should be the same as the length in the header). + * + * Input: Pointer to the buffer of the ciphertext to be decrypted. + * Pointer to the pointer to the buffer receiving the plaintext. + * The pointer either points to user allocated output buffer space, or to NULL, in which case + * this routine will set the pointer to the buffer allocated through the struct. + * The length of the ciphertext buffer. + * Can be -1 if the input is a null terminated binhex string, in which case we'll count for you. + * Boolean flag for BinHex Input (if used, plaintext will be half as large as input). + * Note: BinHex conversion overwrites (converts) input buffer! + * The TwoFish structure. + * + * Output: The amount of bytes decrypted if successful, otherwise 0. + */ +unsigned long TwoFishDecrypt(char *in,char **out,signed long len,bool binhex,TWOFISH *tfdata); + + +/**** Private Functions ****/ + +unsigned char TwoFish__b(unsigned long x,int n); +void _TwoFish_BinHex(unsigned char *buf,unsigned long len,bool bintohex); +unsigned long _TwoFish_CryptRawCBC(char *in,char *out,unsigned long len,bool decrypt,TWOFISH *tfdata); +unsigned long _TwoFish_CryptRaw16(char *in,char *out,unsigned long len,bool decrypt,TWOFISH *tfdata); +unsigned long _TwoFish_CryptRaw(char *in,char *out,unsigned long len,bool decrypt,TWOFISH *tfdata); +void _TwoFish_PrecomputeMDSmatrix(void); +void _TwoFish_MakeSubKeys(TWOFISH *tfdata); +void _TwoFish_qBlockPush(unsigned char *p,unsigned char *c,TWOFISH *tfdata); +void _TwoFish_qBlockPop(unsigned char *p,unsigned char *c,TWOFISH *tfdata); +void _TwoFish_ResetCBC(TWOFISH *tfdata); +void _TwoFish_FlushOutput(unsigned char *b,unsigned long len,TWOFISH *tfdata); +void _TwoFish_BlockCrypt(unsigned char *in,unsigned char *out,unsigned long size,int decrypt,TWOFISH *tfdata); +void _TwoFish_BlockCrypt16(unsigned char *in,unsigned char *out,bool decrypt,TWOFISH *tfdata); +unsigned long _TwoFish_RS_MDS_Encode(unsigned long k0,unsigned long k1); +unsigned long _TwoFish_F32(unsigned long k64Cnt,unsigned long x,unsigned long *k32); +unsigned long _TwoFish_Fe320(unsigned long *lsBox,unsigned long x); +unsigned long _TwoFish_Fe323(unsigned long *lsBox,unsigned long x); +unsigned long _TwoFish_Fe32(unsigned long *lsBox,unsigned long x,unsigned long R); + + +#endif