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

spp_telnet_negotiation.c

Go to the documentation of this file.
00001 /*
00002 ** Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
00003 **
00004 ** This program is free software; you can redistribute it and/or modify
00005 ** it under the terms of the GNU General Public License as published by
00006 ** the Free Software Foundation; either version 2 of the License, or
00007 ** (at your option) any later version.
00008 **
00009 ** This program is distributed in the hope that it will be useful,
00010 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 ** GNU General Public License for more details.
00013 **
00014 ** You should have received a copy of the GNU General Public License
00015 ** along with this program; if not, write to the Free Software
00016 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017 */
00018 
00019 /* Snort Preprocessor for Telnet Negotiation Normalization*/
00020 /* $Id$ */
00021 
00022 /* spp_telnet_negotiation.c 
00023  * 
00024  * Purpose:  Telnet and FTP sessions can contain telnet negotiation strings 
00025  *           that can disrupt pattern matching.  This plugin detects 
00026  *           negotiation strings in stream and "normalizes" them much like
00027  *           the http_decode preprocessor normalizes encoded URLs
00028  *
00029  *
00030  * http://www.iana.org/assignments/telnet-options  -- official registry of options
00031  *
00032  *
00033  * Arguments:  None
00034  *   
00035  * Effect:  The telnet nogiation data is removed from the payload
00036  *
00037  * Comments:
00038  *
00039  */
00040 
00041 /* your preprocessor header file goes here */
00042 
00043 #ifdef HAVE_CONFIG_H
00044 #include "config.h"
00045 #endif
00046 
00047 #ifdef HAVE_STRINGS_H
00048 #include <strings.h>
00049 #endif
00050 
00051 #include <sys/types.h>
00052 
00053 #include "decode.h"
00054 #include "plugbase.h"
00055 #include "parser.h"
00056 #include "log.h"
00057 #include "debug.h"
00058 #include "util.h"
00059 #include "mstring.h"
00060 #include "snort.h"
00061 
00062 extern u_int8_t DecodeBuffer[DECODE_BLEN]; /* decode.c */
00063 
00064 /* define the telnet negotiation codes (TNC) that we're interested in */
00065 #define TNC_IAC  0xFF
00066 #define TNC_SB   0xFA
00067 #define TNC_GA   0xF9
00068 #define TNC_EAL  0xF8
00069 #define TNC_EAC  0xF7
00070 #define TNC_AO   0xF6
00071 #define TNC_AYT  0xF5
00072 #define TNC_IP   0xF4
00073 #define TNC_BRK  0xF3
00074 #define TNC_DM   0xF2
00075 #define TNC_NOP  0xF1
00076 #define TNC_SE   0xF0
00077 
00078 #define TNC_STD_LENGTH  3
00079 
00080 /* list of function prototypes for this preprocessor */
00081 extern void TelNegInit(u_char *);
00082 extern void NormalizeTelnet(Packet *, void *);
00083 static void SetTelnetPorts(char *portlist);
00084      
00085 /* array containing info about which ports we care about */
00086 static char TelnetDecodePorts[65536/8];
00087 
00088 
00089 
00090 /*
00091  * Function: SetupTelNeg()
00092  *
00093  * Purpose: Registers the preprocessor keyword and initialization 
00094  *          function into the preprocessor list.  
00095  *
00096  * Arguments: None.
00097  *
00098  * Returns: void function
00099  *
00100  */
00101 void SetupTelNeg()
00102 {
00103     /* Telnet negotiation has many names, but we only implement this
00104      * plugin for Bob Graham's benefit...
00105      */ 
00106     RegisterPreprocessor("telnet_decode", TelNegInit);
00107 
00108     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Preprocessor: Telnet Decode Decode is setup...\n"););
00109 }
00110 
00111 
00112 /*
00113  * Function: TelNegInit(u_char *)
00114  *
00115  * Purpose: Calls the argument parsing function, performs final setup on data
00116  *          structs, links the preproc function into the function list.
00117  *
00118  * Arguments: args => ptr to argument string
00119  *
00120  * Returns: void function
00121  *
00122  */
00123 void TelNegInit(u_char *args)
00124 {
00125     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Preprocessor: TelNeg Initialized\n"););
00126 
00127     SetTelnetPorts(args);
00128     /* Set the preprocessor function into the function list */
00129     AddFuncToPreprocList(NormalizeTelnet);
00130 }
00131 
00132 
00133 /*
00134  * Function: PreprocFunction(Packet *)
00135  *
00136  * Purpose: Perform the preprocessor's intended function.  This can be
00137  *          simple (statistics collection) or complex (IP defragmentation)
00138  *          as you like.  Try not to destroy the performance of the whole
00139  *          system by trying to do too much....
00140  *
00141  * Arguments: p => pointer to the current packet data struct 
00142  *
00143  * Returns: void function
00144  *
00145  */
00146 void NormalizeTelnet(Packet *p, void *context)
00147 {
00148     char *read_ptr;
00149     char *start = (char *) DecodeBuffer; /* decode.c */
00150     char *write_ptr;
00151     char *end;
00152     int normalization_required = 0;
00153 
00154     if(!(p->preprocessors & PP_TELNEG))
00155     {
00156         return;
00157     }
00158     
00159     /* check for TCP traffic that's part of an established session */
00160     if(!PacketIsTCP(p))
00161     {
00162         return;
00163     }
00164 
00165     /* check the port list */
00166     if(!(TelnetDecodePorts[(p->dp/8)] & (1<<(p->dp%8))))
00167     {
00168         return;
00169     }
00170 
00171     /* negotiation strings are at least 3 bytes long */
00172     /* other telnet commands are handled in here, too, and
00173      * They can be 2 bytes long -- ie, IAC NOP, IAC AYT, etc. */
00174     if(p->dsize < TNC_STD_LENGTH)
00175     {
00176         return;
00177     }
00178 
00179     /* setup the pointers */
00180     read_ptr = p->data;
00181     end = p->data + p->dsize;
00182     
00183     /* look to see if we have any telnet negotiaion codes in the payload */
00184     while(!normalization_required && (read_ptr < end))
00185     {
00186         /* look for the start of a negotiation string */
00187         if(*read_ptr == (char) TNC_IAC)
00188         {
00189             /* set a flag for stage 2 normalization */
00190             normalization_required = 1;
00191         }
00192 
00193         read_ptr++;
00194     }
00195 
00196     if(!normalization_required)
00197     {
00198         DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Nothing to process!\n"););
00199         return;
00200     }
00201 
00202     /*
00203      * if we found telnet negotiation strings OR backspace characters,
00204      * we're going to have to normalize the data
00205      *
00206      * Note that this is always ( now: 2002-08-12 ) done to a
00207      * alternative data buffer.
00208      */    
00209     /* rewind the data stream to p->data */
00210     read_ptr = p->data;
00211     
00212     /* setup for overwriting the negotaiation strings with 
00213      * the follow-on data
00214      */ 
00215     write_ptr = (char *) DecodeBuffer;
00216     
00217     /* walk thru the remainder of the packet */
00218     while((read_ptr < end) && (write_ptr < ((char *) DecodeBuffer) + DECODE_BLEN))
00219     {         
00220         /* if the following byte isn't a subnegotiation initialization */
00221         if(((read_ptr + 1) < end) &&
00222            (*read_ptr == (char) TNC_IAC) &&
00223            (*(read_ptr + 1) != (char) TNC_SB))
00224         {
00225             /* NOPs are two bytes long */
00226             switch(* ((unsigned char *)(read_ptr + 1)))
00227             {
00228             case TNC_NOP:
00229                 read_ptr += 2;
00230                 break;
00231             case TNC_EAC:
00232                 read_ptr += 2;
00233                 /* wind it back a character */
00234                 if(write_ptr  > start)
00235                 {
00236                     write_ptr--;
00237                 }
00238                 break;
00239             case TNC_EAL:
00240                 /* todo: wind write_ptr back a line? */
00241                 read_ptr += 2;
00242                 break;
00243             case TNC_SE:
00244                 /* Uh, what the heck is a Subnegotiation-end
00245                  * doing here without a SB?.
00246                  * could generate an alert, or just normalize
00247                  * it out.
00248                  */
00249                 read_ptr += 2;
00250                 break;
00251             /* These are two bytes long */
00252             case TNC_BRK:
00253             case TNC_DM:
00254             case TNC_IP:
00255             case TNC_AO:
00256             case TNC_AYT:
00257             case TNC_GA:
00258                 read_ptr += 2;
00259                 break;
00260             /* IAC IAC -- means the IAC character (0xff) should be
00261              * in the data stream since it was escaped */
00262             case TNC_IAC:
00263                 read_ptr++; /* skip past the first IAC */
00264                 *write_ptr++ = *read_ptr++;
00265                 break;
00266             default: /* WILL, WON'T, DO, DON'T */
00267                 /* move the read ptr up 3 bytes */
00268                 read_ptr += TNC_STD_LENGTH;
00269             }                
00270         }
00271         /* check for subnegotiation */
00272         else if(((read_ptr + 1) < end) &&
00273                 (*read_ptr == (char) TNC_IAC) &&
00274                 (*(read_ptr+1) == (char) TNC_SB))
00275         {
00276             /* move to the end of the subneg */
00277             do
00278             {
00279                 read_ptr++;
00280             } while((*read_ptr != (char) TNC_SE) && (read_ptr < end));
00281             if (*read_ptr == (char)TNC_SE)
00282                 read_ptr++; /* Skip past the TNC_SE */
00283         }
00284         else
00285         {
00286             DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "overwriting %2X(%c) with %2X(%c)\n",
00287                                     (char)(*write_ptr&0xFF), *write_ptr, 
00288                                     (char)(*read_ptr & 0xFF), *read_ptr););
00289             
00290             /* overwrite the negotiation bytes with the follow-on bytes */
00291             *write_ptr++ = *read_ptr++;
00292         }
00293     }
00294     
00295     p->packet_flags |= PKT_ALT_DECODE;
00296     
00297     p->alt_dsize = write_ptr - start;
00298     
00299     /* DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, 
00300                             "Converted buffer after telnet normalization:\n");
00301                PrintNetData(stdout, (char *) DecodeBuffer, p->alt_dsize););
00302     */
00303     return;
00304 }
00305 
00306 /*
00307  * Function: SetTelnetPorts(char *)
00308  *
00309  * Purpose: Reads the list of port numbers from the argument string and 
00310  *          parses them into the port list data struct
00311  *
00312  * Arguments: portlist => argument list
00313  *
00314  * Returns: void function
00315  *
00316  */
00317 static void SetTelnetPorts(char *portlist)
00318 {
00319     char portstr[STD_BUF];
00320     char **toks;
00321     int is_reset = 0;
00322     int num_toks = 0;
00323     int num = 0;
00324 
00325     if(portlist == NULL || *portlist == '\0')
00326     {
00327         portlist = "21 23 25 119";
00328     }
00329     
00330     /* tokenize the argument list */
00331     toks = mSplit(portlist, " ", 31, &num_toks, '\\');
00332 
00333     LogMessage("telnet_decode arguments:\n");
00334 
00335     /* convert the tokens and place them into the port list */
00336     for(num = 0; num < num_toks; num++)
00337     {
00338         if(isdigit((int)toks[num][0]))
00339         {
00340             char *num_p = NULL; /* used to determine last position in string */
00341             long t_num;
00342         
00343             t_num = strtol(toks[num], &num_p, 10);
00344         
00345             if(*num_p != '\0')
00346             {
00347                 FatalError("Port Number invalid format: %s\n", toks[num]);
00348             }
00349             else if(t_num < 0 || t_num > 65335)
00350             {
00351                 FatalError("Port Number out of range: %ld\n", t_num);
00352             }
00353         
00354             /* user specified a legal port number and it should override the default
00355                port list, so reset it unless already done */
00356             if(!is_reset)
00357             {
00358                 bzero(&TelnetDecodePorts, sizeof(TelnetDecodePorts));
00359                 portstr[0] = '\0';
00360                 is_reset = 1;
00361             }
00362         
00363             /* mark this port as being interesting using some portscan2-type voodoo, 
00364                and also add it to the port list string while we're at it so we can
00365                later print out all the ports with a single LogMessage() */
00366             TelnetDecodePorts[(t_num/8)] |= 1<<(t_num%8);
00367 
00368             if(strlcat(portstr, toks[num], STD_BUF - 1) >= STD_BUF)
00369             {
00370                 FatalError("%s(%d) Portstr is truncated!\n", file_name, file_line);
00371             }
00372                         
00373             if(strlcat(portstr, " ", STD_BUF - 1) >= STD_BUF)
00374             {
00375                 FatalError("%s(%d) Portstr is truncated!\n", file_name, file_line);
00376             }
00377         }
00378         else
00379         {
00380             FatalError(" %s(%d) => Unknown argument to telnet_decode "
00381                        "preprocessor: \"%s\"\n", 
00382                        file_name, file_line, toks[num]);
00383         }
00384     }
00385     
00386     mSplitFree(&toks, num_toks);
00387 
00388     /* print out final port list */
00389     LogMessage("    Ports to decode telnet on: %s\n", portstr);
00390 }

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