"Fossies" - the Fresh Open Source Software Archive

Member "snort-2.9.17/src/dynamic-preprocessors/pop/pop_paf.c" (16 Oct 2020, 14593 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 "pop_paf.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 /****************************************************************************
    2  * Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
    3  * Copyright (C) 2011-2013 Sourcefire, Inc.
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of the GNU General Public License Version 2 as
    7  * published by the Free Software Foundation.  You may not use, modify or
    8  * distribute this program under any other version of the GNU General
    9  * Public License.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU General Public License
   17  * along with this program; if not, write to the Free Software
   18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   19  *
   20  ****************************************************************************/
   21 
   22 
   23 #include <sys/types.h>
   24 #include "sf_types.h"
   25 #include "pop_paf.h"
   26 #include "spp_pop.h"
   27 #include "sf_dynamic_preprocessor.h"
   28 #include "stream_api.h"
   29 #include "snort_pop.h"
   30 #include "pop_config.h"
   31 #include "file_api.h"
   32 #include "pop_util.h"
   33 
   34 static uint8_t pop_paf_id = 0;
   35 
   36 // global variables defined in snort_pop.c
   37 extern const POPToken pop_known_cmds[];
   38 
   39 /* Structure used to record expected server termination sequence */
   40 typedef enum _PopExpectedResp
   41 {
   42     POP_PAF_SINGLE_LINE_STATE,  /* server response will end with \r\n */
   43     POP_PAF_MULTI_LINE_STATE,   /* server response will end with \r\n.\r\n */
   44     POP_PAF_DATA_STATE,         /* Indicated MIME will be contained in response */
   45     POP_PAF_HAS_ARG             /* Intermediate state when parsing LIST */
   46 } PopExpectedResp;
   47 
   48 
   49 typedef enum _PopParseCmdState
   50 {
   51     POP_CMD_SEARCH,     /* Search for Command */
   52     POP_CMD_FIN,        /* Found space. Finished parsing Command */
   53     POP_CMD_ARG         /* Parsing command with multi-line response iff arg given */
   54 } PopParseCmdState;
   55 
   56 
   57 /* saves data when parsing client commands */
   58 typedef struct _PopPafParseCmd
   59 {
   60     char *next_letter;          /* a pointer to the current commands data */
   61     PopExpectedResp exp_resp;   /* the expected termination sequence for this command */
   62     PopParseCmdState status;    /* whether the current has already been found */
   63 } PopPafParseCmd;
   64 
   65 
   66 /* State tracker for POP PAF */
   67 typedef struct _PopPafData
   68 {
   69     PopExpectedResp pop_state;   /* The current POP PAF state. */
   70     DataEndState end_state;   /* Current termination sequence state */
   71     PopPafParseCmd cmd_state;    /* all of the command parsing data */
   72     MimeDataPafInfo data_info;   /* Mime Information */
   73     bool cmd_continued;          /* data continued from previous packet? */
   74     bool end_of_data;
   75 //    uint32_t length;           /* TODO --> use length in top and RETR */
   76 } PopPafData;
   77 
   78 
   79 
   80 /*
   81  *  read process_command() description below
   82  */
   83 static bool search_for_command(PopPafData *pfdata, const uint8_t ch)
   84 {
   85     char val = *(pfdata->cmd_state.next_letter);
   86 
   87     // if end of command && data contains a space or newline
   88     if (val == '\0'  && (isblank(ch) || ch == '\r' || ch == '\n'))
   89     {
   90         if (pfdata->cmd_state.exp_resp == POP_PAF_HAS_ARG)
   91         {
   92             pfdata->cmd_state.status = POP_CMD_ARG;
   93         }
   94         else 
   95         {
   96             pfdata->cmd_state.status = POP_CMD_FIN;
   97             pfdata->pop_state = pfdata->cmd_state.exp_resp;
   98             return true;
   99         }
  100     }
  101     else if (toupper(ch) == toupper(val) )
  102     {
  103         pfdata->cmd_state.next_letter++;
  104     }
  105     else
  106     {
  107         pfdata->cmd_state.status = POP_CMD_FIN;
  108     }
  109 
  110     return false;
  111 }
  112 
  113 
  114 /*
  115  *  read process_command() description below
  116  */
  117 static bool init_command_search(PopPafData *pfdata, const uint8_t ch)
  118 {
  119     pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
  120 
  121     switch(ch)
  122     {
  123     case 'c':
  124     case 'C':
  125         pfdata->cmd_state.exp_resp = POP_PAF_MULTI_LINE_STATE;
  126         pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_CAPA].name[1]);
  127         break;
  128     case 'l':
  129     case 'L':
  130         pfdata->cmd_state.exp_resp = POP_PAF_HAS_ARG;
  131         pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_LIST].name[1]);
  132         break;
  133     case 'r':
  134     case 'R':
  135         pfdata->cmd_state.exp_resp = POP_PAF_DATA_STATE;
  136         pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_RETR].name[1]);
  137         break;
  138     case 't':
  139     case 'T':
  140         pfdata->cmd_state.exp_resp = POP_PAF_DATA_STATE;
  141         pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_TOP].name[1]);
  142         break;
  143     case 'u':
  144     case 'U':
  145         pfdata->cmd_state.exp_resp = POP_PAF_HAS_ARG;
  146         pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_UIDL].name[1]);
  147         break;
  148     default:
  149         pfdata->cmd_state.status = POP_CMD_FIN;
  150     }
  151 
  152     return false;
  153 }
  154 
  155 /*
  156  * Attempts to determine the current command based upon the given character
  157  * If another character is required to determine the current command,
  158  * sets the function pointer to the correct next state
  159  *
  160  * PARAMS:
  161  *         pop_cmd - a pointer to the struct containing all of the 
  162  *                     relevant parsing info
  163  *         ch      - the first character from the clients command
  164  * RETURNS
  165  *         true  - if the expected response is NOT a single line
  166  *         false - otherwise
  167  */
  168 static inline bool process_command(PopPafData *pfdata, const uint8_t ch)
  169 {
  170     if (pfdata->cmd_state.next_letter)
  171         return search_for_command(pfdata, ch);
  172     else
  173         return init_command_search(pfdata, ch);
  174 }
  175 
  176 
  177 static inline void reset_data_states(PopPafData *pfdata)
  178 {
  179     // reset MIME info
  180     _dpd.fileAPI->reset_mime_paf_state(&(pfdata->data_info));
  181 
  182     // reset general pop fields
  183     pfdata->cmd_continued = false;
  184     pfdata->end_state = PAF_DATA_END_UNKNOWN;
  185     pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
  186 }
  187 
  188 
  189 /*
  190  *  Checks if the current data is a valid response.
  191  *  According to RFC 1939, every response begins with either 
  192  *     +OK
  193  *     -ERR.
  194  *  
  195  *  RETURNS:
  196  *           true - if the character is a +
  197  *           false - if the character is anything else
  198  */ 
  199 static inline int valid_response(const uint8_t data)
  200 {
  201     return (data == '+');
  202 }
  203 
  204 /*
  205  * Client PAF calls this command to set the server's state.  This is the
  206  * function which ensure's the server know the correct expected
  207  * DATA
  208  */
  209 static inline void set_server_state(void *ssn, PopExpectedResp state)
  210 {
  211     PopPafData *server_data = *(_dpd.streamAPI->get_paf_user_data(ssn, 0, pop_paf_id));
  212 
  213     // ERROR IF SERVER DATA DOES NOT EXIST!! SHOULD NOT BE POSSIBLE!!
  214     if (server_data)
  215     {
  216         reset_data_states(server_data);
  217         server_data->end_of_data = false;
  218         server_data->pop_state = state;
  219     }
  220 }
  221 
  222 /*
  223  * A helper function to reset the client's command parsing 
  224  * information 
  225  */
  226 static inline void reset_client_cmd_info(PopPafData *pfdata)
  227 {
  228     pfdata->cmd_state.next_letter = NULL;
  229     pfdata->cmd_state.status = POP_CMD_SEARCH;
  230 }
  231 
  232 /*
  233  * Statefully search for the termination sequence CRCL.CRLF ("\r\n.\r\n").  
  234  *
  235  * PARAMS:
  236  *        mime_data : true if this is mime_data.
  237  *
  238  * RETURNS:
  239  *         0 - if termination sequence not found 
  240  *         1 - if termination sequence found
  241  */
  242 static bool find_data_end_multi_line(PopPafData *pfdata, const uint8_t ch, bool mime_data)
  243 {
  244     // TODO:  This will currently flush on MIME boundary, and one line later at end of PDU
  245 
  246     if (_dpd.fileAPI->check_data_end(&(pfdata->end_state), ch))
  247     {
  248         DEBUG_WRAP(DebugMessage(DEBUG_POP, "End of Multi-line response found\n"););
  249         pfdata->end_of_data = true;
  250         pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
  251         reset_data_states(pfdata);
  252         return true;
  253     }
  254 
  255     // if this is a data command, search for MIME ending
  256     if (mime_data)
  257     {
  258         if (_dpd.fileAPI->process_mime_paf_data(&(pfdata->data_info), ch))
  259         {
  260             DEBUG_WRAP(DebugMessage(DEBUG_POP, "Mime Boundary found.  Flushing data!\n"););
  261             pfdata->cmd_continued = true;
  262             return true;
  263         }
  264     }
  265 
  266 
  267     return false;
  268 }
  269 
  270 /*
  271  * Statefully search for the termination sequence LF ("\n").  Will also
  272  * set the correct response state.
  273  *
  274  * PARAMS:
  275  *
  276  * RETURNS:
  277  *         0 - if terminatino sequence not found 
  278  *         1 - if termination sequence found
  279  */
  280 static inline bool find_data_end_single_line(PopPafData *pfdata, const uint8_t ch, bool client)
  281 {
  282     if (ch == '\n')
  283     {
  284         // reset the correct information
  285         if (client)
  286             reset_client_cmd_info(pfdata);
  287         else
  288             reset_data_states(pfdata);
  289 
  290         DEBUG_WRAP(DebugMessage(DEBUG_POP, "End of single-line response "
  291                 "found.  Flushing data!\n"););
  292         return true;
  293     }
  294 
  295     return false;
  296 }
  297 
  298 
  299 static PAF_Status pop_paf_server(PopPafData *pfdata,
  300         const uint8_t* data, uint32_t len, uint32_t* fp)
  301 {
  302     uint32_t i;
  303     uint32_t boundary_start = 0;
  304 
  305     // if a negative response was received, it will be a one line response.
  306     if (!pfdata->cmd_continued && !valid_response(*data))
  307         pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
  308 
  309 
  310     for (i = 0; i < len; i++)
  311     {
  312         uint8_t ch = data[i];
  313 
  314         // find the termination sequence based upon the current state
  315         switch (pfdata->pop_state)
  316         {
  317         case POP_PAF_MULTI_LINE_STATE:
  318             if( find_data_end_multi_line(pfdata, ch, false) )
  319             {
  320                 *fp = i + 1;
  321                 return PAF_FLUSH;
  322             }
  323             break;
  324         
  325         case POP_PAF_DATA_STATE:
  326             // TODO --> statefully get length
  327             if ( find_data_end_multi_line(pfdata, ch, true) )
  328             {
  329                 *fp = i + 1;
  330                 return PAF_FLUSH;
  331             }
  332 
  333             if (pfdata->data_info.boundary_state == MIME_PAF_BOUNDARY_UNKNOWN)
  334                 boundary_start = i;
  335 
  336             break;
  337 
  338         case POP_PAF_SINGLE_LINE_STATE:
  339         default:
  340             if ( find_data_end_single_line(pfdata, ch, false) )
  341             {
  342                 *fp = i + 1;
  343                 return PAF_FLUSH;
  344             }
  345             break;
  346         }
  347     }
  348 
  349     pfdata->cmd_continued = true;    
  350 
  351 
  352     if ( scanning_boundary(&pfdata->data_info, boundary_start, fp) )
  353         return PAF_LIMIT;
  354 
  355     return PAF_SEARCH;
  356 }
  357 
  358 
  359 /*
  360  * Determine the Client's command and set the response state.
  361  * Flush data when "\r\n" is received
  362  */
  363 static PAF_Status pop_paf_client(void *ssn, PopPafData *pfdata,
  364         const uint8_t* data, uint32_t len, uint32_t* fp)
  365 {
  366     uint32_t i;
  367 
  368     // TODO ... ensure current command is smaller than max command length
  369 
  370     for (i = 0; i < len; i++)
  371     {
  372         uint8_t ch = data[i];
  373 
  374         switch (pfdata->cmd_state.status)
  375         {
  376         case POP_CMD_SEARCH:
  377             if (process_command(pfdata, ch) )
  378             {
  379                 set_server_state(ssn, pfdata->pop_state);
  380             }
  381             
  382             //break;  DO NOT UNCOMMENT!!  both cases should check for a LF.
  383         
  384         case POP_CMD_FIN:
  385             if (find_data_end_single_line(pfdata, ch, true) )
  386             {
  387                 // reset command parsing data
  388                 *fp = i + 1;
  389                 return PAF_FLUSH;
  390             }
  391             break;
  392 
  393         case POP_CMD_ARG:
  394             if (find_data_end_single_line(pfdata, ch, true))
  395             {
  396                 set_server_state(ssn, POP_PAF_MULTI_LINE_STATE);
  397                 *fp = i + 1;
  398                 return PAF_FLUSH;
  399             }
  400             else if (isdigit(ch))
  401             {
  402                 pfdata->cmd_state.status = POP_CMD_FIN;
  403             }
  404         }
  405     }
  406 
  407     return PAF_SEARCH;
  408 }
  409 
  410 /* Function: pop_paf()
  411 
  412    Purpose: POP PAF callback.
  413             Inspects pop traffic.  Checks client traffic for the current command
  414             and sets correct server termination sequence. Client side data will
  415             flush after receiving CRLF ("\r\n").  Server data flushes after 
  416             finding set termination sequence.
  417 
  418    Arguments:
  419      void * - stream5 session pointer
  420      void ** - DNP3 state tracking structure
  421      const uint8_t * - payload data to inspect
  422      uint32_t - length of payload data
  423      uint32_t - flags to check whether client or server
  424      uint32_t * - pointer to set flush point
  425      uint32_t * - pointer to set header flush point
  426 
  427    Returns:
  428     PAF_Status - PAF_FLUSH if flush point found, PAF_SEARCH otherwise
  429 */
  430 
  431 static PAF_Status pop_paf(void* ssn, void** ps, const uint8_t* data,
  432         uint32_t len, uint64_t *flags, uint32_t* fp, uint32_t* fp_eoh)
  433 {
  434 
  435     PopPafData *pfdata = *(PopPafData **)ps;
  436 
  437     if (pfdata == NULL)
  438     {
  439         if (_dpd.fileAPI->check_paf_abort(ssn))
  440             return PAF_ABORT;
  441         
  442         pfdata = _dpd.snortAlloc(1, sizeof(*pfdata), PP_POP, 0);
  443         
  444         if (pfdata == NULL)
  445         {
  446             return PAF_ABORT;
  447         }
  448 
  449         reset_data_states(pfdata);
  450         *ps = pfdata;
  451     }
  452 
  453 
  454     if (*flags & FLAG_FROM_SERVER)
  455     {
  456        DEBUG_WRAP(DebugMessage(DEBUG_POP, "PAF: From server.\n"););
  457         return pop_paf_server(pfdata, data, len, fp);
  458     }
  459     else
  460     {
  461         DEBUG_WRAP(DebugMessage(DEBUG_POP, "PAF: From client.\n"););
  462         return pop_paf_client(ssn, pfdata, data, len, fp);
  463     }
  464 }
  465 
  466 bool is_data_end (void* ssn)
  467 {
  468     if ( ssn )
  469     {
  470         PopPafData** s = (PopPafData **)_dpd.streamAPI->get_paf_user_data(ssn, 0, pop_paf_id);
  471 
  472         if ( s && (*s) )
  473             return ((*s)->end_of_data);
  474     }
  475 
  476     return false;
  477 }
  478 
  479 void pop_paf_cleanup(void *pafData)
  480 {
  481    if (pafData) {
  482       _dpd.snortFree(pafData, sizeof(PopPafData), PP_POP, 0);
  483    }
  484 }
  485 
  486 #ifdef TARGET_BASED
  487 void register_pop_paf_service (struct _SnortConfig *sc, int16_t app, tSfPolicyId policy)
  488 {
  489     if (_dpd.isPafEnabled())
  490     {
  491        pop_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, true, pop_paf, true);
  492        pop_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, false,pop_paf, true);
  493        _dpd.streamAPI->register_paf_free(pop_paf_id, pop_paf_cleanup);
  494     }
  495 }
  496 #endif
  497 
  498 
  499 void register_pop_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy)
  500 {
  501     if (_dpd.isPafEnabled())
  502     {
  503         pop_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, true, pop_paf, true);
  504         pop_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, false, pop_paf, true);
  505     _dpd.streamAPI->register_paf_free(pop_paf_id, pop_paf_cleanup);
  506     }
  507 }