"Fossies" - the Fresh Open Source Software Archive

Member "snort-2.9.17/src/dynamic-preprocessors/smtp/smtp_paf.c" (16 Oct 2020, 13697 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 "smtp_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) 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 #include <sys/types.h>
   22 #include "sf_types.h"
   23 #include "smtp_paf.h"
   24 #include "spp_smtp.h"
   25 #include "sf_dynamic_preprocessor.h"
   26 #include "stream_api.h"
   27 #include "snort_smtp.h"
   28 #include "file_api.h"
   29 #include "smtp_log.h"
   30 
   31 static uint8_t smtp_paf_id = 0;
   32 
   33 extern tSfPolicyUserContextId smtp_config;
   34 
   35 /* State tracker for MIME PAF */
   36 typedef enum _SmtpDataCMD
   37 {
   38     SMTP_PAF_BDAT_CMD,
   39     SMTP_PAF_DATA_CMD,
   40     SMTP_PAF_XEXCH50_CMD,
   41     SMTP_PAF_STRARTTLS_CMD,
   42     SMTP_PAF_AUTH_CMD
   43 } SmtpDataCMD;
   44 
   45 typedef struct _PAFToken
   46 {
   47     char *name;
   48     int   name_len;
   49     int   search_id;
   50     bool  has_length;
   51 } SmtpPAFToken;
   52 
   53 const SmtpPAFToken smtp_paf_tokens[] =
   54 {
   55         {"BDAT",         4, SMTP_PAF_BDAT_CMD, true},
   56         {"DATA",         4, SMTP_PAF_DATA_CMD, true},
   57         {"XEXCH50",      7, SMTP_PAF_XEXCH50_CMD, true},
   58         {"STRARTTLS",    9, SMTP_PAF_STRARTTLS_CMD, false},
   59         {"AUTH",         4, SMTP_PAF_AUTH_CMD, false},
   60         {NULL,           0, 0, false}
   61 };
   62 
   63 /* State tracker for data command */
   64 typedef enum _SmtpPafCmdState
   65 {
   66     SMTP_PAF_CMD_UNKNOWN,
   67     SMTP_PAF_CMD_START,
   68     SMTP_PAF_CMD_DETECT,
   69     SMTP_PAF_CMD_DATA_LENGTH_STATE,
   70     SMTP_PAF_CMD_DATA_END_STATE
   71 } SmtpPafCmdState;
   72 
   73 /* State tracker for SMTP PAF */
   74 typedef enum _SmtpPafState
   75 {
   76     SMTP_PAF_CMD_STATE,
   77     SMTP_PAF_DATA_STATE
   78 } SmtpPafState;
   79 
   80 typedef struct _SmtpCmdSearchInfo
   81 {
   82     SmtpPafCmdState cmd_state;
   83     int   search_id;
   84     char *search_state;
   85 } SmtpCmdSearchInfo;
   86 
   87 /* State tracker for SMTP PAF */
   88 typedef struct _SmtpPafData
   89 {
   90     DataEndState data_end_state;
   91     uint32_t length;
   92     SmtpPafState smtp_state;
   93     bool end_of_data;
   94     bool alert_generated;
   95     SmtpCmdSearchInfo cmd_info;
   96     MimeDataPafInfo data_info;
   97 } SmtpPafData;
   98 
   99 /* State tracker for SMTP PAF */
  100 typedef enum _SmtpPafDataLenStatus
  101 {
  102     SMTP_PAF_LENGTH_INVALID,
  103     SMTP_PAF_LENGTH_CONTINUE,
  104     SMTP_PAF_LENGTH_DONE
  105 } SmtpPafDataLenStatus;
  106 
  107 /* Process responses from server, flushed at EOL*/
  108 static inline PAF_Status smtp_paf_server(SmtpPafData *pfdata,
  109         const uint8_t* data, uint32_t len, uint32_t* fp)
  110 {
  111     const char *pch;
  112 
  113     pfdata->smtp_state = SMTP_PAF_CMD_STATE;
  114     pch = memchr (data, '\n', len);
  115 
  116     if (pch != NULL)
  117     {
  118         DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Find end of line!\n"););
  119         *fp = (uint32_t)(pch - (const char*)data) + 1;
  120         return PAF_FLUSH;
  121     }
  122     return PAF_SEARCH;
  123 }
  124 
  125 /* Initialize command search based on first byte of command*/
  126 static inline char* init_cmd_search(SmtpCmdSearchInfo *search_info,  uint8_t ch)
  127 {
  128 
  129     /* Use the first byte to choose data command)*/
  130     switch (ch)
  131     {
  132     case 'b':
  133     case 'B':
  134         search_info->search_state = &smtp_paf_tokens[SMTP_PAF_BDAT_CMD].name[1];
  135         search_info->search_id = SMTP_PAF_BDAT_CMD;
  136         break;
  137     case 'd':
  138     case 'D':
  139         search_info->search_state = &smtp_paf_tokens[SMTP_PAF_DATA_CMD].name[1];
  140         search_info->search_id = SMTP_PAF_DATA_CMD;
  141         break;
  142     case 'x':
  143     case 'X':
  144         search_info->search_state = &smtp_paf_tokens[SMTP_PAF_XEXCH50_CMD].name[1];
  145         search_info->search_id = SMTP_PAF_XEXCH50_CMD;
  146         break;
  147     case 's':
  148     case 'S':
  149         search_info->search_state = &smtp_paf_tokens[SMTP_PAF_STRARTTLS_CMD].name[1];
  150         search_info->search_id = SMTP_PAF_STRARTTLS_CMD;
  151         break;
  152     case 'a':
  153     case 'A':
  154         search_info->search_state = &smtp_paf_tokens[SMTP_PAF_AUTH_CMD].name[1];
  155         search_info->search_id = SMTP_PAF_AUTH_CMD;
  156         break;
  157 
  158     default:
  159         search_info->search_state = NULL;
  160         break;
  161     }
  162     return search_info->search_state;
  163 }
  164 
  165 /* Validate whether the command is a data command*/
  166 static inline void validate_command(SmtpCmdSearchInfo *search_info,  uint8_t val)
  167 {
  168     if (search_info->search_state )
  169     {
  170         uint8_t expected = *(search_info->search_state);
  171 
  172         if (toupper(val) == toupper(expected))
  173         {
  174             search_info->search_state++;
  175             /* Found data command, change to SMTP_PAF_CMD_DATA_LENGTH_STATE */
  176             if (*(search_info->search_state) == '\0')
  177             {
  178                 search_info->search_state = NULL;
  179                 search_info->cmd_state = SMTP_PAF_CMD_DATA_LENGTH_STATE;
  180                 return;
  181             }
  182         }
  183         else
  184         {
  185             search_info->search_state = NULL;
  186             search_info->cmd_state = SMTP_PAF_CMD_UNKNOWN;
  187             return;
  188         }
  189     }
  190 }
  191 
  192 /* Get the length of data from data command
  193  */
  194 static SmtpPafDataLenStatus get_length(char c, uint32_t *len )
  195 {
  196     uint32_t length = *len;
  197 
  198     if (isblank(c))
  199     {
  200         if (length)
  201         {
  202             *len = length;
  203             return SMTP_PAF_LENGTH_DONE;
  204         }
  205     }
  206     else if (isdigit(c))
  207     {
  208         uint64_t tmp_len = (10 * length)  + (c - '0');
  209         if (tmp_len < UINT32_MAX)
  210             length = (uint32_t) tmp_len;
  211         else
  212         {
  213             *len = 0;
  214             return SMTP_PAF_LENGTH_INVALID;
  215         }
  216     }
  217     else
  218     {
  219         *len = 0;
  220         return SMTP_PAF_LENGTH_INVALID;
  221     }
  222 
  223     *len = length;
  224     return SMTP_PAF_LENGTH_CONTINUE;
  225 }
  226 
  227 /* Reset data states*/
  228 static inline void reset_data_states(SmtpPafData *pfdata)
  229 {
  230     _dpd.fileAPI->reset_mime_paf_state(&(pfdata->data_info));
  231     pfdata->length = 0;
  232 }
  233 
  234 /* Currently, we support "BDAT", "DATA", "XEXCH50", "STRARTTLS"
  235  * Each data command should start from offset 0,
  236  * since previous data have been flushed
  237  */
  238 static inline bool process_command(SmtpPafData *pfdata,  uint8_t val)
  239 {
  240     /*State unknown, start cmd search start from EOL, flush on EOL*/
  241     if (val == '\n')
  242     {
  243         if (pfdata->cmd_info.cmd_state == SMTP_PAF_CMD_DATA_END_STATE)
  244         {
  245             pfdata->smtp_state = SMTP_PAF_DATA_STATE;
  246             reset_data_states(pfdata);
  247             pfdata->end_of_data = false;
  248         }
  249 
  250         pfdata->cmd_info.cmd_state = SMTP_PAF_CMD_START;
  251         return 1;
  252     }
  253 
  254     switch (pfdata->cmd_info.cmd_state)
  255     {
  256     case SMTP_PAF_CMD_UNKNOWN:
  257         break;
  258     case SMTP_PAF_CMD_START:
  259         if (init_cmd_search(&(pfdata->cmd_info), val))
  260             pfdata->cmd_info.cmd_state = SMTP_PAF_CMD_DETECT;
  261         else
  262             pfdata->cmd_info.cmd_state = SMTP_PAF_CMD_UNKNOWN;
  263         break;
  264     case SMTP_PAF_CMD_DETECT:
  265         /* Search for data command */
  266         validate_command(&(pfdata->cmd_info), val);
  267         break;
  268     case SMTP_PAF_CMD_DATA_LENGTH_STATE:
  269         /* Continue finding the data length ...*/
  270         if (get_length(val, &pfdata->length) != SMTP_PAF_LENGTH_CONTINUE)
  271         {
  272             DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Find data length: %d\n",
  273                     pfdata->length););
  274             pfdata->cmd_info.cmd_state = SMTP_PAF_CMD_DATA_END_STATE;
  275         }
  276         break;
  277     case SMTP_PAF_CMD_DATA_END_STATE:
  278         /* Change to Data state at EOL*/
  279         break;
  280     default:
  281         break;
  282     }
  283 
  284     return 0;
  285 }
  286 
  287 /* Flush based on data length*/
  288 static inline bool flush_based_length(SmtpPafData *pfdata)
  289 {
  290     if (pfdata->length)
  291     {
  292         pfdata->length--;
  293         if (!pfdata->length)
  294             return true;
  295     }
  296     return false;
  297 }
  298 
  299 /* Process data length if specified, or end of data marker, flush at the end
  300  * or
  301  * Process data boundary and flush each file based on boundary*/
  302 static inline bool process_data(SmtpPafData *pfdata,  uint8_t data)
  303 {
  304 
  305     if (flush_based_length(pfdata)||
  306             _dpd.fileAPI->check_data_end(&(pfdata->data_end_state), data))
  307     {
  308         DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "End of data\n"););
  309         /*Clean up states*/
  310         pfdata->smtp_state = SMTP_PAF_CMD_STATE;
  311         pfdata->end_of_data = true;
  312         reset_data_states(pfdata);
  313         return true;
  314     }
  315 
  316     return _dpd.fileAPI->process_mime_paf_data(&(pfdata->data_info), data);
  317 }
  318 
  319 /* Process commands/data from client
  320  * For command, flush at EOL
  321  * For data, flush at boundary
  322  */
  323 static inline PAF_Status smtp_paf_client(SmtpPafData *pfdata,
  324         const uint8_t* data, uint32_t len, uint32_t* fp)
  325 {
  326     uint32_t i;
  327     uint32_t boundary_start = 0;
  328     tSfPolicyId policy_id = _dpd.getNapRuntimePolicy();
  329     SMTPConfig* smtp_current_config = (SMTPConfig *)sfPolicyUserDataGet(smtp_config, policy_id);
  330 
  331     DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "From client: %s \n", data););
  332     for (i = 0; i < len; i++)
  333     {
  334         uint8_t ch = data[i];
  335         switch (pfdata->smtp_state)
  336         {
  337         case SMTP_PAF_CMD_STATE:
  338             if (process_command(pfdata, ch))
  339             {
  340                 DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Flush command: %s \n", data););
  341                 *fp = i + 1;
  342                 return PAF_FLUSH;
  343             }
  344             break;
  345         case SMTP_PAF_DATA_STATE:
  346             if (pfdata->cmd_info.search_id == SMTP_PAF_AUTH_CMD)
  347             {
  348                 if ( smtp_current_config && (smtp_current_config->max_auth_command_line_len != 0) &&
  349                     (i + pfdata->data_info.boundary_len) > smtp_current_config->max_auth_command_line_len &&
  350                     !pfdata->alert_generated)
  351                 {
  352                     if( !smtp_current_config->no_alerts )
  353                     {
  354                         _dpd.alertAdd(GENERATOR_SMTP, SMTP_AUTH_COMMAND_OVERFLOW, 1, 0, 3,
  355                                 SMTP_AUTH_COMMAND_OVERFLOW_STR, 0);
  356                         pfdata->alert_generated = true;
  357                     }
  358                 }
  359                 if (ch == '\n')
  360                 {
  361                     pfdata->smtp_state = SMTP_PAF_CMD_STATE;
  362                     pfdata->end_of_data = true;
  363                     reset_data_states(pfdata);
  364                     *fp = i + 1;
  365                     return PAF_FLUSH;
  366                 }
  367                 else if (i == len-1)
  368                     pfdata->data_info.boundary_len += len;
  369             }
  370             else if (process_data(pfdata, ch))
  371             {
  372                 DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Flush data!\n"););
  373                 *fp = i + 1;
  374                 return PAF_FLUSH;
  375             }
  376 
  377             if (pfdata->data_info.boundary_state == MIME_PAF_BOUNDARY_UNKNOWN)
  378                 boundary_start = i;
  379             break;
  380         default:
  381             break;
  382         }
  383     }
  384 
  385 
  386     if ( scanning_boundary(&pfdata->data_info, boundary_start, fp) )
  387         return PAF_LIMIT;
  388 
  389     return PAF_SEARCH;
  390 }
  391 
  392 /* Main PAF function*/
  393 static PAF_Status smtp_paf_eval(void* ssn, void** ps, const uint8_t* data,
  394         uint32_t len, uint64_t *flags, uint32_t* fp, uint32_t* fp_eoh)
  395 {
  396     SmtpPafData *pfdata = *(SmtpPafData **)ps;
  397 
  398     if (pfdata == NULL)
  399     {
  400         if (_dpd.fileAPI->check_paf_abort(ssn))
  401             return PAF_ABORT;
  402 
  403         pfdata = _dpd.snortAlloc(1, sizeof(*pfdata), PP_SMTP, 0);
  404 
  405         if (pfdata == NULL)
  406         {
  407             return PAF_ABORT;
  408         }
  409 
  410         *ps = pfdata;
  411         pfdata->data_end_state = PAF_DATA_END_UNKNOWN;
  412         pfdata->smtp_state = SMTP_PAF_CMD_STATE;
  413         pfdata->alert_generated = false;
  414     }
  415 
  416     /*From server side (responses)*/
  417     if (*flags & FLAG_FROM_SERVER)
  418     {
  419         DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "From server!\n"););
  420         return smtp_paf_server(pfdata, data, len, fp);
  421     }
  422     else /*From client side (requests)*/
  423     {
  424         DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "From client!\n"););
  425         return smtp_paf_client(pfdata, data, len, fp);
  426     }
  427 
  428     return PAF_SEARCH;
  429 }
  430 
  431 bool is_data_end (void* ssn)
  432 {
  433     if ( ssn )
  434     {
  435         SmtpPafData** s = (SmtpPafData **)_dpd.streamAPI->get_paf_user_data(ssn, 1, smtp_paf_id);
  436 
  437         if ( s && (*s) )
  438             return ((*s)->end_of_data);
  439     }
  440 
  441     return false;
  442 }
  443 
  444 void smtp_paf_cleanup(void *pafData)
  445 {
  446     if (pafData) {
  447     _dpd.snortFree(pafData, sizeof(SmtpPafData), PP_SMTP, 0);
  448     }
  449 }
  450 
  451 #ifdef TARGET_BASED
  452 void register_smtp_paf_service (struct _SnortConfig *sc, int16_t app, tSfPolicyId policy)
  453 {
  454     if (_dpd.isPafEnabled())
  455     {
  456         smtp_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, true, smtp_paf_eval, true);
  457         smtp_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, false,smtp_paf_eval, true);
  458     _dpd.streamAPI->register_paf_free(smtp_paf_id, smtp_paf_cleanup);
  459     }
  460 }
  461 #endif
  462 
  463 void register_smtp_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy)
  464 {
  465     if (_dpd.isPafEnabled())
  466     {
  467         smtp_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, true, smtp_paf_eval, true);
  468         smtp_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, false, smtp_paf_eval, true);
  469     _dpd.streamAPI->register_paf_free(smtp_paf_id, smtp_paf_cleanup);
  470     }
  471 }
  472