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

sp_pcre.c

Go to the documentation of this file.
00001 /* $Id$ */
00002 /*
00003 ** Copyright (C) 2003 Brian Caswell <bmc@snort.org>
00004 ** Copyright (C) 2003 Michael J. Pomraning <mjp@securepipe.com>
00005 ** Copyright (C) 2003 Sourcefire, Inc
00006 ** 
00007 ** This program is free software; you can redistribute it and/or modify
00008 ** it under the terms of the GNU General Public License as published by
00009 ** the Free Software Foundation; either version 2 of the License, or
00010 ** (at your option) any later version.
00011 **
00012 ** This program is distributed in the hope that it will be useful,
00013 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 ** GNU General Public License for more details.
00016 **
00017 ** You should have received a copy of the GNU General Public License
00018 ** along with this program; if not, write to the Free Software
00019 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 #include "config.h"
00024 #endif
00025 
00026 #include "bounds.h"
00027 #include "rules.h"
00028 #include "debug.h"
00029 #include "decode.h"
00030 #include "plugbase.h"
00031 #include "parser.h"
00032 #include "plugin_enum.h"
00033 #include "util.h"
00034 #include "mstring.h"
00035 #include <sys/types.h>
00036 
00037 #ifdef WIN32
00038 #define PCRE_DEFINITION
00039 #endif
00040 
00041 #include <pcre.h>
00042 
00043 typedef struct _PcreData
00044 {
00045     pcre *re;           /* compiled regex */
00046     pcre_extra *pe;     /* studied regex foo */
00047     int options;        /* sp_pcre specfic options (relative & inverse) */
00048 } PcreData;
00049 
00050 
00051 #define SNORT_PCRE_RELATIVE 1  /* relative to the end of the last match */
00052 #define SNORT_PCRE_INVERT   2  /* invert detect */
00053 #define SNORT_PCRE_URI      4  /* check URI buffers */
00054 #define SNORT_PCRE_RAWBYTES 8  /* Don't use decoded buffer (if available) */
00055 
00056 /* 
00057  * we need to specify the vector length for our pcre_exec call.  we only care 
00058  * about the first vector, which if the match is successful will include the
00059  * offset to the end of the full pattern match.  If we decide to store other
00060  * matches, make *SURE* that this is a multiple of 3 as pcre requires it.
00061  */
00062 #define SNORT_PCRE_OVECTOR_SIZE 3
00063 
00064 extern u_int8_t DecodeBuffer[DECODE_BLEN];
00065 extern u_int8_t *doe_ptr;
00066 
00067 void SnortPcreInit(char *, OptTreeNode *, int);
00068 void SnortPcreParse(char *, PcreData *, OptTreeNode *);
00069 void SnortPcreDump(PcreData *);
00070 int SnortPcre(Packet *, struct _OptTreeNode *, OptFpList *);
00071 
00072 void SetupPcre(void)
00073 {
00074     RegisterPlugin("pcre", SnortPcreInit);
00075 }
00076 
00077 void SnortPcreInit(char *data, OptTreeNode *otn, int protocol)
00078 {
00079     PcreData *pcre_data;
00080     OptFpList *fpl;
00081 
00082     /* 
00083      * allocate the data structure for pcre
00084      */
00085     pcre_data = (PcreData *) SnortAlloc(sizeof(PcreData));
00086 
00087     if(pcre_data == NULL)
00088     {
00089         FatalError("%s (%d): Unable to allocate pcre_data node\n",
00090                    file_name, file_line);
00091     }
00092 
00093     SnortPcreParse(data, pcre_data, otn);
00094 
00095     fpl = AddOptFuncToList(SnortPcre, otn);
00096 
00097     /*
00098      * attach it to the context node so that we can call each instance
00099      * individually
00100      */
00101     fpl->context = (void *) pcre_data;
00102 
00103     return;
00104 }
00105 
00106 void SnortPcreParse(char *data, PcreData *pcre_data, OptTreeNode *otn)
00107 {
00108     const char *error;
00109     char *re, *free_me;
00110     char *opts;
00111     char delimit = '/';
00112     int erroffset;
00113     int compile_flags = 0;
00114     
00115     if(data == NULL) 
00116     {
00117         FatalError("%s (%d): pcre requires a regular expression\n", 
00118                    file_name, file_line);
00119     }
00120 
00121     if(!(free_me = strdup(data)))
00122     {
00123         FatalError("%s (%d): pcre strdup() failed\n", file_name, file_line);
00124     }
00125     re = free_me;
00126 
00127 
00128     /* get rid of starting and ending whitespace */
00129     while (isspace((int)re[strlen(re)-1])) re[strlen(re)-1] = '\0';
00130     while (isspace((int)*re)) re++;
00131 
00132     if(*re == '!') { 
00133         pcre_data->options |= SNORT_PCRE_INVERT;
00134         re++;
00135         while(isspace((int)*re)) re++;
00136     }
00137 
00138     /* now we wrap the RE in double quotes.  stupid snort parser.... */
00139     if(*re != '"') {
00140         printf("It isn't \"\n");
00141         goto syntax;
00142     }
00143     re++;
00144 
00145     if(re[strlen(re)-1] != '"')
00146     {
00147         printf("It isn't \"\n");
00148         goto syntax;
00149     }
00150     
00151     /* remove the last quote from the string */
00152     re[strlen(re) - 1] = '\0';
00153     
00154     /* 'm//' or just '//' */
00155         
00156     if(*re == 'm')
00157     {
00158         re++;
00159         if(! *re) goto syntax;
00160         
00161         /* Space as a ending delimiter?  Uh, no. */
00162         if(isspace((int)*re)) goto syntax;
00163         /* using R would be bad, as it triggers RE */
00164         if(*re == 'R') goto syntax;   
00165 
00166         delimit = *re;
00167     } 
00168     else if(! *re == delimit)
00169         goto syntax;
00170 
00171     /* find ending delimiter, trim delimit chars */
00172     opts = strrchr(re, delimit);
00173     if(!((opts - re) > 1)) /* empty regex(m||) or missing delim not OK */
00174         goto syntax;
00175 
00176     re++;
00177     *opts++ = '\0';
00178 
00179     /* process any /regex/ismxR options */
00180     while(*opts != '\0') {
00181         switch(*opts) {
00182         case 'i':  compile_flags |= PCRE_CASELESS;            break;
00183         case 's':  compile_flags |= PCRE_DOTALL;              break;
00184         case 'm':  compile_flags |= PCRE_MULTILINE;           break;
00185         case 'x':  compile_flags |= PCRE_EXTENDED;            break;
00186             
00187             /* 
00188              * these are pcre specific... don't work with perl
00189              */ 
00190         case 'A':  compile_flags |= PCRE_ANCHORED;            break;
00191         case 'E':  compile_flags |= PCRE_DOLLAR_ENDONLY;      break;
00192         case 'G':  compile_flags |= PCRE_UNGREEDY;            break;
00193 
00194             /*
00195              * these are snort specific don't work with pcre or perl
00196              */
00197         case 'R':  pcre_data->options |= SNORT_PCRE_RELATIVE; break;
00198         case 'U':  pcre_data->options |= SNORT_PCRE_URI;      break;
00199         case 'B':  pcre_data->options |= SNORT_PCRE_RAWBYTES; break;
00200         default:
00201             FatalError("%s (%d): unknown/extra pcre option encountered\n", file_name, file_line);
00202         }
00203         opts++;
00204     }
00205 
00206     if(pcre_data->options & SNORT_PCRE_RELATIVE && 
00207        pcre_data->options & SNORT_PCRE_URI)
00208         FatalError("%s(%d): PCRE unsupported configuration : both relative & uri options specified\n", file_name, file_line);
00209 
00210     
00211     /* now compile the re */
00212     DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "pcre: compiling %s\n", re););
00213     pcre_data->re = pcre_compile(re, compile_flags, &error, &erroffset, NULL);
00214 
00215     if(pcre_data->re == NULL) 
00216     {
00217         FatalError("%s(%d) : pcre compile of \"%s\" failed at offset "
00218                    "%d : %s\n", file_name, file_line, re, erroffset, error);
00219     }
00220 
00221 
00222     /* now study it... */
00223     pcre_data->pe = pcre_study(pcre_data->re, 0, &error);
00224 
00225     if(error != NULL) 
00226     {
00227         FatalError("%s(%d) : pcre study failed : %s\n", file_name, 
00228                    file_line, error);
00229     }
00230 
00231     free(free_me);
00232 
00233     return;
00234 
00235  syntax:
00236     if(free_me) free(free_me);
00237 
00238     FatalError("ERROR %s Line %d => unable to parse pcre regex %s\n", 
00239                file_name, file_line, data);
00240 
00241 }
00242 
00243 /** 
00244  * Perform a search of the PCRE data.
00245  * 
00246  * @param pcre_data structure that options and patterns are passed in
00247  * @param buf buffer to search
00248  * @param len size of buffer
00249  * @param start_offset initial offset into the buffer
00250  * @param found_offset pointer to an integer so that we know where the search ended
00251  *
00252  * *found_offset will be set to -1 when the find is unsucessful OR the routine is inverted
00253  *
00254  * @return 1 when we find the string, 0 when we don't (unless we've been passed a flag to invert)
00255  */
00256 static int pcre_search(const PcreData *pcre_data,
00257                        const char *buf,
00258                        int len,
00259                        int start_offset,
00260                        int *found_offset)
00261 {
00262     int ovector[SNORT_PCRE_OVECTOR_SIZE];
00263     int matched;
00264     int result;
00265     
00266     if(pcre_data == NULL
00267        || buf == NULL
00268        || len <= 0
00269        || start_offset < 0
00270        || start_offset >= len
00271        || found_offset == NULL)
00272     {
00273         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00274                                 "Returning 0 because we didn't have the required parameters!\n"););
00275         return 0;
00276     }
00277 
00278     *found_offset = -1;
00279     
00280     result = pcre_exec(pcre_data->re,            /* result of pcre_compile() */
00281                        pcre_data->pe,            /* result of pcre_study()   */
00282                        buf,                      /* the subject string */
00283                        len,                      /* the length of the subject string */
00284                        start_offset,             /* start at offset 0 in the subject */
00285                        0,                        /* options(handled at compile time */
00286                        ovector,                  /* vector for substring information */
00287                        SNORT_PCRE_OVECTOR_SIZE); /* number of elements in the vector */
00288 
00289     if(result >= 0)
00290     {
00291         matched = 1;
00292     }
00293     else if(result == PCRE_ERROR_NOMATCH)
00294     {
00295         matched = 0;
00296     }
00297     else
00298     {
00299         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "pcre_exec error : %d \n", result););
00300         return 0;
00301     }
00302 
00303     /* invert sense of match */
00304     if(pcre_data->options & SNORT_PCRE_INVERT) 
00305     {
00306         matched = !matched;
00307     }
00308     else
00309     {
00310         
00311         *found_offset = ovector[1];        
00312         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00313                                 "Setting Doe_ptr and found_offset: %p %d\n",
00314                                 doe_ptr, found_offset););
00315     }
00316 
00317     return matched;
00318 }
00319 
00320 int SnortPcre(Packet *p, struct _OptTreeNode *otn, OptFpList *fp_list)
00321 {
00322     PcreData *pcre_data;   /* pointer to the eval string for each test */
00323     int found_offset;  /* where is the ending location of the pattern */
00324     char *base_ptr, *end_ptr, *start_ptr;
00325     int dsize;
00326     int length; /* length of the buffer pointed to by base_ptr  */
00327     int matched = 0;
00328     extern HttpUri UriBufs[URI_COUNT];
00329     int i;
00330 
00331     DEBUG_WRAP(char *hexbuf;);
00332 
00333     /* get my data */
00334     pcre_data =(PcreData *) fp_list->context;
00335 
00336     /* This is the HTTP case */
00337     if(pcre_data->options & SNORT_PCRE_URI) 
00338     {
00339         for(i=0;i<p->uri_count;i++)
00340         {
00341             matched = pcre_search(pcre_data,
00342                                   UriBufs[i].uri,
00343                                   UriBufs[i].length,
00344                                   0,
00345                                   &found_offset);
00346             
00347             if(matched)
00348             {
00349                 /* don't touch doe_ptr on URI contents */
00350                 return fp_list->next->OptTestFunc(p, otn, fp_list->next);
00351             }
00352         }
00353         
00354         return 0;
00355     }
00356     /* end of the HTTP case */
00357 
00358     if(p->packet_flags & PKT_ALT_DECODE && !(pcre_data->options & SNORT_PCRE_RAWBYTES))
00359     {
00360         dsize = p->alt_dsize;
00361         start_ptr = (char *) DecodeBuffer;
00362         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00363                                 "using alternative decode buffer in pcre!\n"););
00364     }
00365     else
00366     {
00367         dsize = p->dsize;
00368         start_ptr = (char *) p->data;
00369     }
00370 
00371     base_ptr = start_ptr;
00372     end_ptr = start_ptr + dsize;
00373 
00374     /* doe_ptr's would be set by the previous content option */
00375     if(pcre_data->options & SNORT_PCRE_RELATIVE && doe_ptr)
00376     {
00377         if(!inBounds(start_ptr, end_ptr, doe_ptr))
00378         {
00379             DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, 
00380                                     "pcre bounds check failed on a relative content match\n"););
00381             return 0;
00382         }
00383         
00384         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00385                                 "pcre ... checking relative offset\n"););
00386         base_ptr = doe_ptr;
00387     }
00388     else
00389     {
00390         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00391                                 "pcre ... checking absolute offset\n"););
00392         base_ptr = start_ptr;
00393     }
00394 
00395     length = end_ptr - base_ptr;
00396     
00397     DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
00398                             "pcre ... base: %p start: %p end: %p doe: %p length: %d\n",
00399                             base_ptr, start_ptr, end_ptr, doe_ptr, length););
00400 
00401     DEBUG_WRAP(hexbuf = hex(base_ptr, length);
00402                DebugMessage(DEBUG_PATTERN_MATCH, "pcre payload: %s\n", hexbuf);
00403                free(hexbuf);
00404                );
00405 
00406 
00407     matched = pcre_search(pcre_data, base_ptr, length, 0, &found_offset);
00408 
00409     /* set the doe_ptr if we have a valid offset */
00410     if(found_offset > 0)
00411     {
00412         doe_ptr = (u_int8_t *) base_ptr + found_offset;
00413     }
00414     
00415     while(matched)
00416     {
00417         int search_offset = found_offset;
00418         int next_found = fp_list->next->OptTestFunc(p, otn, fp_list->next);
00419 
00420         if(next_found)
00421         {
00422             /* if the OTN checks are successful, return 1, else
00423                return the next iteration */
00424             /* set the doe_ptr for stateful pattern matching later */
00425 
00426             doe_ptr = (u_int8_t *) base_ptr + found_offset;
00427 
00428             return 1;
00429         }
00430 
00431         /* the other OTNs search's were not successful so we need to keep searching */
00432         if(search_offset <= 0 || length < search_offset)
00433         {
00434             /* make sure that the search offset is reasonable */
00435             return 0;
00436         }
00437 
00438         matched = pcre_search(pcre_data, base_ptr, length,
00439                               search_offset, &found_offset);
00440 
00441         /* set the doe_ptr if we have a valid offset */
00442         if(found_offset > 0)
00443         {
00444             doe_ptr = (u_int8_t *) base_ptr + found_offset;
00445         }
00446         
00447         if(matched)
00448         {
00449             if(fp_list->next->OptTestFunc(p, otn, fp_list->next))
00450             {
00451                 /* if the OTN checks are successful, return 1, else
00452                    return the next iteration */
00453 
00454                 return 1;
00455             }
00456             
00457         }            
00458     }
00459 
00460     /* finally return 0 */
00461     return 0;
00462 }
00463 

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