"Fossies" - the Fresh Open Source Software Archive

Member "snort-2.9.17/src/detection-plugins/sp_react.c" (16 Oct 2020, 13893 Bytes) of package /linux/misc/snort-2.9.17.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sp_react.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.9.16.1_vs_2.9.17.

    1 /* $Id$ */
    2 /****************************************************************************
    3  *
    4  * Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
    5  * Copyright (C) 2005-2013 Sourcefire, Inc.
    6  *
    7  * This program is free software; you can redistribute it and/or modify
    8  * it under the terms of the GNU General Public License Version 2 as
    9  * published by the Free Software Foundation.  You may not use, modify or
   10  * distribute this program under any other version of the GNU General
   11  * Public License.
   12  *
   13  * This program is distributed in the hope that it will be useful,
   14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  * GNU General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU General Public License
   19  * along with this program; if not, write to the Free Software
   20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   21  *
   22  ****************************************************************************/
   23 
   24 // @file    sp_react.c
   25 // @author  Russ Combs <rcombs@sourcefire.com>
   26 
   27 /* The original Snort React Plugin was contributed by Maciej Szarpak, Warsaw
   28  * University of Technology.  The module has been entirely rewritten by
   29  * Sourcefire as part of the effort to overhaul active response.  Some of the
   30  * changes include:
   31  *
   32  * - elimination of unworkable warn mode
   33  * - elimination of proxy port (rule header has ports)
   34  * - integration with unified active response mechanism
   35  * - queuing of rule option responses so at most one is issued
   36  * - allow override by rule action when action is drop
   37  * - addition of http headers to default response
   38  * - added custom page option
   39  * - and other stuff
   40  *
   41  * This version will send a web page to the client and then reset both
   42  * ends of the session.  The web page may be configured or the default
   43  * may be used.  The web page can have the default warning message
   44  * inserted or the message from the rule.
   45  *
   46  * If you wish to just reset the session, use the resp keyword instead.
   47  */
   48 
   49 #ifdef ENABLE_REACT
   50 
   51 #ifdef HAVE_CONFIG_H
   52 #include "config.h"
   53 #endif
   54 
   55 #include <sys/types.h>
   56 #include <sys/stat.h>
   57 
   58 #include <stdlib.h>
   59 #include <string.h>
   60 #include <ctype.h>
   61 
   62 #include "sf_types.h"
   63 #include "snort_debug.h"
   64 #include "decode.h"
   65 #include "encode.h"
   66 #include "detection_options.h"
   67 #include "parser.h"
   68 #include "plugbase.h"
   69 #include "plugin_enum.h"
   70 #include "profiler.h"
   71 #include "active.h"
   72 #include "rules.h"
   73 #include "sfhashfcn.h"
   74 #include "sp_react.h"
   75 #include "snort.h"
   76 
   77 #ifdef PERF_PROFILING
   78 static PreprocStats reactPerfStats;
   79 extern PreprocStats ruleOTNEvalPerfStats;
   80 #endif
   81 
   82 static const char* MSG_KEY = "<>";
   83 static const char* MSG_PERCENT = "%";
   84 
   85 static const char* DEFAULT_HTTP =
   86     "HTTP/1.1 403 Forbidden\r\n"
   87     "Connection: close\r\n"
   88     "Content-Type: text/html; charset=utf-8\r\n"
   89     "Content-Length: %d\r\n"
   90     "\r\n";
   91 
   92 static const char* DEFAULT_HTML =
   93     "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\r\n"
   94     "    \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\r\n"
   95     "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\r\n"
   96     "<head>\r\n"
   97     "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\r\n"
   98     "<title>Access Denied</title>\r\n"
   99     "</head>\r\n"
  100     "<body>\r\n"
  101     "<h1>Access Denied</h1>\r\n"
  102     "<p>%s</p>\r\n"
  103     "</body>\r\n"
  104     "</html>\r\n";
  105 
  106 static const char* DEFAULT_MSG =
  107     "You are attempting to access a forbidden site.<br />"
  108     "Consult your system administrator for details.";
  109 
  110 typedef struct _ReactData
  111 {
  112     uint32_t id;
  113     int rule_msg;        // 1=>use rule msg; 0=>use DEFAULT_MSG
  114     ssize_t buf_len;     // length of response
  115     char* resp_buf;      // response to send
  116     const OptTreeNode* otn;
  117 
  118 } ReactData;
  119 
  120 static int s_init = 1;
  121 static int s_deprecated = 0;
  122 static char* s_page = NULL;
  123 
  124 // When React_Init() is called the rule msg keyword may not have
  125 // been processed.  This necessitates two things:
  126 //
  127 // * A unique instance id is used in the hash in lieu of the
  128 //   message text.  The id starts at 1 since 0 is reserved for
  129 //   the default msg.  Assuming all rules have different msg
  130 //   strings, the id is a valid proxy.
  131 //
  132 // * React_Config() is installed to instantiate the page after
  133 //   rule parsing is complete (when for sure the msg is
  134 //   available).
  135 //
  136 // Ideally a separate rule configuration callback could be installed
  137 // that would be called after all options are parsed and before the
  138 // options are finalized.
  139 static uint32_t s_id = 1;
  140 
  141 // callback functions
  142 static void React_Init(struct _SnortConfig *, char *, OptTreeNode *, int);
  143 static void React_Cleanup(int signal, void *data);
  144 static void React_Config (struct _SnortConfig *, void *data);
  145 
  146 // core functions
  147 static void React_GetPage(struct _SnortConfig *);
  148 static void React_Parse(char *, OptTreeNode *, ReactData *);
  149 static int React_Queue(Packet*, void*);
  150 static void React_Send(Packet*,  void*);
  151 
  152 //--------------------------------------------------------------------
  153 // public functions
  154 
  155 void ReactFree(void *d)
  156 {
  157     ReactData *data = (ReactData *)d;
  158     if (data->resp_buf)
  159         free(data->resp_buf);
  160     free(data);
  161 }
  162 
  163 uint32_t ReactHash(void *d)
  164 {
  165     uint32_t a,b,c,tmp;
  166     unsigned int i,j,k,l;
  167     ReactData *data = (ReactData *)d;
  168 
  169     const char* s = s_page ? s_page : DEFAULT_HTML;
  170     unsigned n = strlen(s);
  171 
  172     a = data->rule_msg;
  173     b = n;
  174     c = (data->rule_msg ? data->id : 0);
  175 
  176     mix(a,b,c);
  177 
  178     for ( i=0,j=0; i<n; i+=4 )
  179     {
  180         tmp = 0;
  181         k = n - i;
  182         if (k > 4)
  183             k=4;
  184 
  185         for (l=0;l<k;l++)
  186         {
  187             tmp |= s[i + l] << l*8;
  188         }
  189 
  190         switch (j)
  191         {
  192             case 0:
  193                 a += tmp;
  194                 break;
  195             case 1:
  196                 b += tmp;
  197                 break;
  198             case 2:
  199                 c += tmp;
  200                 break;
  201         }
  202         j++;
  203 
  204         if (j == 3)
  205         {
  206             mix(a,b,c);
  207             j = 0;
  208         }
  209     }
  210 
  211     if (j != 0)
  212     {
  213         mix(a,b,c);
  214     }
  215 
  216     a += RULE_OPTION_TYPE_REACT;
  217 
  218     final(a,b,c);
  219 
  220     return c;
  221 }
  222 
  223 int ReactCompare(void *l, void *r)
  224 {
  225     ReactData *left = (ReactData *)l;
  226     ReactData *right = (ReactData *)r;
  227 
  228     if (!left || !right)
  229         return DETECTION_OPTION_NOT_EQUAL;
  230 
  231     if (left->buf_len != right->buf_len)
  232         return DETECTION_OPTION_NOT_EQUAL;
  233 
  234     if (memcmp(left->resp_buf, right->resp_buf, left->buf_len) != 0)
  235         return DETECTION_OPTION_NOT_EQUAL;
  236 
  237     if (left->rule_msg != right->rule_msg)
  238         return DETECTION_OPTION_NOT_EQUAL;
  239 
  240     return DETECTION_OPTION_EQUAL;
  241 }
  242 
  243 void SetupReact(void)
  244 {
  245     RegisterRuleOption("react", React_Init, NULL, OPT_TYPE_ACTION, NULL);
  246 #ifdef PERF_PROFILING
  247     RegisterPreprocessorProfile("react", &reactPerfStats, 3, &ruleOTNEvalPerfStats, NULL);
  248 #endif
  249 }
  250 
  251 //--------------------------------------------------------------------
  252 // callback functions
  253 
  254 static void React_Init(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
  255 {
  256     ReactData* rd;
  257     void *idx_dup;
  258 
  259     if ( otn->ds_list[PLUGIN_RESPONSE] )
  260         FatalError("%s(%d): Multiple response options in rule\n",
  261             file_name, file_line);
  262 
  263     if ( protocol != IPPROTO_TCP )
  264         FatalError("%s(%d): React options on non-TCP rule\n",
  265             file_name, file_line);
  266 
  267     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"In React_Init()\n"););
  268 
  269     if ( s_init )
  270     {
  271         AddFuncToCleanExitList(React_Cleanup, NULL);
  272 
  273         React_GetPage(sc);
  274 
  275         Active_SetEnabled(1);
  276         s_init = 0;
  277     }
  278 
  279     /* parse the react keywords */
  280     rd = SnortAlloc(sizeof(*rd));
  281     React_Parse(data, otn, rd);
  282     rd->otn = otn;
  283 
  284     if (add_detection_option(sc, RULE_OPTION_TYPE_REACT, (void*)rd, &idx_dup)
  285         == DETECTION_OPTION_EQUAL)
  286     {
  287         free(rd);
  288         rd = idx_dup;
  289     }
  290     /* finally, attach the option's detection function to the rule's
  291        detect function pointer list */
  292     AddRspFuncToList(React_Queue, otn, (void*)rd);
  293     AddFuncToPreprocPostConfigList(sc, React_Config, rd);
  294 
  295     // this prevents multiple response options in rule
  296     otn->ds_list[PLUGIN_RESPONSE] = rd;
  297 }
  298 
  299 static void React_Cleanup(int signal, void* data)
  300 {
  301     if ( s_page )
  302     {
  303         free(s_page);
  304         s_page = NULL;
  305     }
  306     s_init = 1;
  307 }
  308 
  309 //--------------------------------------------------------------------
  310 // core functions
  311 
  312 static void React_GetPage (struct _SnortConfig *sc)
  313 {
  314     char* msg;
  315     char* percent_s;
  316     struct stat fs;
  317     FILE* fd;
  318     size_t n;
  319 
  320     if ( !sc )
  321         FatalError("react: %s(%d) Snort config for parsing is NULL.\n",
  322             file_name, file_line);
  323 
  324     if ( s_page || !sc->react_page ) return;
  325 
  326     if ( stat(sc->react_page, &fs) )
  327         FatalError("react: %s(%d) can't stat react page file '%s'.\n",
  328             file_name, file_line, sc->react_page);
  329 
  330     if ( fs.st_size < 2 )
  331         FatalError("react: react page %s size is not adequate.\n",
  332             sc->react_page);
  333 
  334     s_page = SnortAlloc(fs.st_size+1);
  335     fd = fopen(sc->react_page, "r");
  336 
  337     if ( !fd )
  338         FatalError("react: %s(%d) can't open react page file '%s'.\n",
  339             file_name, file_line, sc->react_page);
  340 
  341     n = fread(s_page, 1, fs.st_size, fd);
  342     fclose(fd);
  343 
  344     if ( n != (size_t)fs.st_size )
  345         FatalError("react: %s(%d) can't load react page file '%s'.\n",
  346             file_name, file_line, sc->react_page);
  347 
  348     s_page[n] = '\0';
  349 
  350     msg = strstr(s_page, MSG_KEY);
  351     if ( msg ) strncpy(msg, "%s", 2);
  352 
  353     // search for %
  354     percent_s = strstr(s_page, MSG_PERCENT);
  355     if (percent_s)
  356     {
  357         percent_s += strlen(MSG_PERCENT); // move past current
  358         // search for % again
  359         percent_s = strstr(percent_s, MSG_PERCENT);
  360         if (percent_s)
  361         {
  362             FatalError("react: %s(%d) can't specify more than one %%s or other "
  363                 "printf style formatting characters in react page '%s'.\n",
  364                 file_name, file_line, sc->react_page);
  365         }
  366     }
  367 }
  368 
  369 //--------------------------------------------------------------------
  370 
  371 static void React_Parse(char* data, OptTreeNode* otn, ReactData* rd)
  372 {
  373     char* tok = NULL;
  374 
  375     if ( data )
  376     {
  377         while(isspace((int)*data)) data++;
  378 
  379         tok = strtok(data, ",");
  380     }
  381     while(tok)
  382     {
  383         /* parse the react option keywords */
  384         if (
  385             !strncasecmp(tok, "proxy", 5) ||
  386             !strcasecmp(tok, "block") ||
  387             !strcasecmp(tok, "warn") )
  388         {
  389             if ( !s_deprecated )
  390             {
  391                 ParseWarning("proxy, block, and warn options are deprecated.\n");
  392                 s_deprecated = 1;
  393             }
  394         }
  395         else if ( !strcasecmp(tok, "msg") )
  396         {
  397             rd->rule_msg = 1;
  398         }
  399         else
  400             FatalError("%s(%d): invalid react option: %s\n",
  401                 file_name, file_line, tok);
  402 
  403         tok = strtok(NULL, ",");
  404 
  405         /* get rid of spaces */
  406         while ( tok && isspace((int)*tok) ) tok++;
  407     }
  408     rd->resp_buf = NULL;
  409     rd->buf_len = 0;
  410     rd->id = s_id++;
  411 }
  412 
  413 //--------------------------------------------------------------------
  414 // format response buffer
  415 
  416 static void React_Config (struct _SnortConfig *sc, void *data)
  417 {
  418     ReactData *rd = (ReactData *)data;
  419     size_t body_len, head_len, total_len;
  420     char dummy;
  421 
  422     const char* head = DEFAULT_HTTP;
  423     const char* body = s_page ? s_page : DEFAULT_HTML;
  424 
  425     const char* msg = rd->otn->sigInfo.message;
  426     if ( !msg || !rd->rule_msg ) msg = DEFAULT_MSG;
  427 
  428     body_len = snprintf(&dummy, 1, body, msg);
  429     head_len = snprintf(&dummy, 1, head, body_len);
  430     total_len = head_len + body_len + 1;
  431 
  432     rd->resp_buf = (char*)SnortAlloc(total_len);
  433 
  434     SnortSnprintf((char*)rd->resp_buf, head_len+1, head, body_len);
  435     SnortSnprintf((char*)rd->resp_buf+head_len, body_len+1, body, msg);
  436 
  437     // set actual length
  438     rd->resp_buf[total_len-1] = '\0';
  439     rd->buf_len = strlen(rd->resp_buf);
  440 }
  441 
  442 //--------------------------------------------------------------------
  443 
  444 static int React_Queue (Packet* p, void* pv)
  445 {
  446     ReactData* rd = (ReactData*)pv;
  447     PROFILE_VARS;
  448 
  449     PREPROC_PROFILE_START(reactPerfStats);
  450 
  451     if ( Active_IsRSTCandidate(p) )
  452         Active_QueueResponse(React_Send, rd);
  453 
  454     Active_DropSession(p);
  455     if (pkt_trace_enabled)
  456     {
  457         if (rd && rd->otn)
  458             addPktTraceData(VERDICT_REASON_REACT, snprintf(trace_line, MAX_TRACE_LINE,
  459                 "Snort React: web page %s, gid %u, sid %u, %s\n", Active_IsRSTCandidate(p)? "is sent" : "isn't sent",
  460                 rd->otn->sigInfo.generator, rd->otn->sigInfo.id, getPktTraceActMsg()));
  461         else addPktTraceData(VERDICT_REASON_REACT, snprintf(trace_line, MAX_TRACE_LINE,
  462                 "Snort React: web page %s, %s\n", Active_IsRSTCandidate(p)? "is sent" : "isn't sent", getPktTraceActMsg()));
  463     }
  464     else addPktTraceData(VERDICT_REASON_REACT, 0);
  465 
  466     PREPROC_PROFILE_END(reactPerfStats);
  467     return 0;
  468 }
  469 
  470 //--------------------------------------------------------------------
  471 
  472 static void React_Send (Packet* p,  void* pv)
  473 {
  474     ReactData* rd = (ReactData*)pv;
  475     EncodeFlags df = (p->packet_flags & PKT_FROM_SERVER) ? ENC_FLAG_FWD : 0;
  476     EncodeFlags sent = rd->buf_len;
  477     EncodeFlags rf;
  478     PROFILE_VARS;
  479 
  480     PREPROC_PROFILE_START(reactPerfStats);
  481     Active_IgnoreSession(p);
  482 
  483     if (p->packet_flags & PKT_STREAM_EST)
  484     {
  485         Active_SendData(p, df, (uint8_t*)rd->resp_buf, rd->buf_len);
  486         // Active_SendData sends a FIN, so need to bump seq by 1.
  487         sent++;
  488     }
  489     rf = df ^ ENC_FLAG_FWD;
  490     df |= ENC_FLAG_SEQ | (ENC_FLAG_VAL & sent);
  491     Active_SendReset(p, df);
  492     Active_SendReset(p, rf);
  493 
  494     PREPROC_PROFILE_END(reactPerfStats);
  495 }
  496 
  497 #endif /* ENABLE_REACT */
  498