"Fossies" - the Fresh Open Source Software Archive

Member "snort-2.9.17/src/dynamic-preprocessors/sdf/spp_sdf.c" (16 Oct 2020, 33757 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 "spp_sdf.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) 2009-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 #ifdef HAVE_CONFIG_H
   22 #include "config.h"
   23 #endif
   24 
   25 #include <assert.h>
   26 #include <sys/types.h>
   27 #include <stdlib.h>
   28 #include <ctype.h>
   29 #include <string.h>
   30 #include <errno.h>
   31 #include <limits.h>
   32 
   33 #ifdef HAVE_STRINGS_H
   34 #include <strings.h>
   35 #endif
   36 
   37 #include "sf_types.h"
   38 /*
   39 #include "snort.h"
   40 #include "parser.h"
   41 #include "util.h"
   42 #include "plugbase.h"
   43 */
   44 #include "snort_debug.h"
   45 #include "stream_api.h"
   46 #include "sfPolicy.h"
   47 #include "sfPolicyUserData.h"
   48 #include "sf_snort_packet.h"
   49 
   50 /*
   51 #ifdef TARGET_BASED
   52 #include "sftarget_protocol_reference.h"
   53 #endif
   54 */
   55 
   56 #include "profiler.h"
   57 
   58 #include "spp_sdf.h"
   59 #include "sf_preproc_info.h"
   60 #include "sdf_us_ssn.h"
   61 #include "sdf_detection_option.h"
   62 #include "sdf_pattern_match.h"
   63 
   64 const int MAJOR_VERSION = 1;
   65 const int MINOR_VERSION = 1;
   66 const int BUILD_VERSION = 1;
   67 const char *PREPROC_NAME = "SF_SDF";
   68 
   69 #define SetupSDF DYNAMIC_PREPROC_SETUP
   70 
   71 /* PROTOTYPES */
   72 static void SDFInit(struct _SnortConfig *, char *args);
   73 static void ProcessSDF(void *p, void *context);
   74 static SDFConfig * NewSDFConfig(struct _SnortConfig *, tSfPolicyUserContextId);
   75 static void ParseSDFArgs(SDFConfig *config, char *args);
   76 static void SDFCleanExit(int signal, void *unused);
   77 static int SDFFreeConfig(tSfPolicyUserContextId context, tSfPolicyId id, void *pData);
   78 static void SDFFillPacket(sdf_tree_node *node, SDFSessionData *session,
   79                           SFSnortPacket *p, uint16_t *dlen);
   80 static void SDFPrintPseudoPacket(SDFConfig *config, SDFSessionData *session,
   81                                  SFSnortPacket *real_packet);
   82 
   83 #ifdef SNORT_RELOAD
   84 static void SDFReload(struct _SnortConfig *, char *, void **);
   85 static void * SDFReloadSwap(struct _SnortConfig *, void *);
   86 static void SDFReloadSwapFree(void *);
   87 #endif
   88 
   89 /* GLOBALS :( */
   90 SDFContext *sdf_context = NULL;
   91 static uint32_t sdf_config_count = 0;
   92 
   93 #ifdef SNORT_RELOAD
   94 sdf_tree_node *swap_head_node = NULL;
   95 uint32_t swap_num_patterns = 0;
   96 #endif
   97 
   98 #ifdef PERF_PROFILING
   99 PreprocStats sdfPerfStats;
  100 #endif
  101 
  102 #define IPPROTO_SDF 0xFE  // TBD - use same for ps?  (eg IPPROTO_SNORT?)
  103 
  104 /*
  105  * Function: SetupSDF()
  106  *
  107  * Purpose: Registers the preprocessor keyword and initialization function
  108  *          into the preprocessor list.
  109  *
  110  * Arguments: None.
  111  *
  112  * Returns: void
  113  *
  114  */
  115 void SetupSDF(void)
  116 {
  117 #ifndef SNORT_RELOAD
  118     _dpd.registerPreproc("sensitive_data", SDFInit);
  119 #else
  120     _dpd.registerPreproc("sensitive_data", SDFInit, SDFReload, NULL, SDFReloadSwap,
  121                          SDFReloadSwapFree);
  122 #endif
  123 }
  124 
  125 /*
  126  * Function: SDFInit(char *)
  127  *
  128  * Purpose: Processes the args sent to the preprocessor, sets up the port list,
  129  *          links the processing function into the preproc function list
  130  *
  131  * Arguments: args => ptr to argument string
  132  *
  133  * Returns: void
  134  *
  135  */
  136 void SDFInit(struct _SnortConfig *sc, char *args)
  137 {
  138     SDFConfig *config = NULL;
  139 
  140     /* Check prerequisites */
  141     if (_dpd.streamAPI == NULL)
  142     {
  143         DynamicPreprocessorFatalMessage("SDFInit(): The Stream preprocessor must be enabled.\n");
  144     }
  145 
  146     /* Create context id, register callbacks. This is only done once. */
  147     if (sdf_context == NULL)
  148     {
  149         sdf_context = (SDFContext *)calloc(1, sizeof(*sdf_context));
  150         if (!sdf_context)
  151             DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF "
  152                                             "configuration.\n");
  153         sdf_context->context_id = sfPolicyConfigCreate();
  154         if (!sdf_context->context_id)
  155             DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF "
  156                                             "configuration.\n");
  157         sdf_context->head_node = (sdf_tree_node *)calloc(1, sizeof(*sdf_context->head_node));
  158         if (!sdf_context->head_node)
  159             DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF "
  160                                             "configuration.\n");
  161 
  162         _dpd.addPreprocExit(SDFCleanExit, NULL, PRIORITY_LAST, PP_SDF);
  163 
  164 #ifdef PERF_PROFILING
  165         _dpd.addPreprocProfileFunc("sensitive_data", (void *)&sdfPerfStats, 0, _dpd.totalPerfStats, NULL);
  166 #endif
  167     }
  168 
  169     /* Handle configuration. This is done once for each policy. */
  170     config = NewSDFConfig(sc, sdf_context->context_id);
  171     ParseSDFArgs(config, args);
  172 
  173     /* Register callbacks */
  174     _dpd.addDetect(sc, ProcessSDF, PRIORITY_FIRST, PP_SDF,
  175                     PROTO_BIT__TCP | PROTO_BIT__UDP);
  176     _dpd.preprocOptRegister(sc, SDF_OPTION_NAME, SDFOptionInit, SDFOptionEval,
  177                             NULL, NULL, NULL, SDFOtnHandler, NULL);
  178 }
  179 
  180 
  181 /* Check the ports and target-based protocol for a given packet.
  182  *
  183  * Returns: 0 if the port check fails
  184  *          1 if the packet should be inspected
  185  */
  186 static int SDFCheckPorts(SDFConfig *config, SFSnortPacket *packet)
  187 {
  188 #ifdef TARGET_BASED
  189     int16_t app_ordinal = SFTARGET_UNKNOWN_PROTOCOL;
  190 
  191     /* Do port checks */
  192     app_ordinal = _dpd.sessionAPI->get_application_protocol_id(packet->stream_session);
  193     if (app_ordinal == SFTARGET_UNKNOWN_PROTOCOL)
  194         return 0;
  195     if (app_ordinal && (config->protocol_ordinals[app_ordinal] == 0))
  196         return 0;
  197     if (app_ordinal == 0)
  198     {
  199 #endif
  200         /* No target-based info for this packet. Check ports. */
  201         if (((config->src_ports[PORT_INDEX(packet->src_port)] & CONV_PORT(packet->src_port)) == 0) ||
  202             ((config->dst_ports[PORT_INDEX(packet->dst_port)] & CONV_PORT(packet->dst_port)) == 0))
  203         {
  204             return 0;
  205         }
  206 #ifdef TARGET_BASED
  207     }
  208 #endif
  209 
  210     return 1;
  211 }
  212 
  213 /* A free function that gets registered along with our Stream session data */
  214 static void FreeSDFSession(void *data)
  215 {
  216     SDFSessionData *session = (SDFSessionData *)data;
  217 
  218     if (session == NULL)
  219         return;
  220 
  221     free(session->counters);
  222     free(session->rtns_matched);
  223     free(session);
  224     return;
  225 }
  226 
  227 /* Create a new SDF session data struct.
  228    Returns:
  229       Fatal Error if allocation fails
  230       Valid ptr otherwise
  231 */
  232 static SDFSessionData * NewSDFSession(SDFConfig *config, SFSnortPacket *packet)
  233 {
  234     SDFSessionData *session;
  235 
  236     /* Allocate new session data. */
  237     session = (SDFSessionData *) malloc(sizeof(SDFSessionData));
  238     if (session == NULL)
  239     {
  240         DynamicPreprocessorFatalMessage("Failed to allocate memory for "
  241                 "SDF preprocessor session data.\n");
  242     }
  243 
  244     if (packet->stream_session)
  245     {
  246         _dpd.sessionAPI->set_application_data(packet->stream_session,
  247                                              PP_SDF, session, FreeSDFSession);
  248     }
  249 
  250     session->part_match_node= NULL;
  251     session->part_match_index = 0;
  252     session->global_counter = 0;
  253     session->config_num = config->config_num;
  254     session->last_pkt_seq_num = 0;
  255     session->last_pkt_data_len = -1;
  256     /* Allocate counters in the session data */
  257     session->num_patterns = sdf_context->num_patterns;
  258     session->counters = calloc(session->num_patterns, sizeof(uint8_t));
  259     session->rtns_matched = calloc(session->num_patterns, sizeof(int8_t));
  260     if (session->counters == NULL || session->rtns_matched == NULL)
  261     {
  262         DynamicPreprocessorFatalMessage("Failed to allocate memory for "
  263                 "SDF preprocessor session data.\n");
  264     }
  265 
  266     return session;
  267 }
  268 
  269 static inline bool isBufferInPayload(char *begin, char *end, SFSnortPacket *packet)
  270 {
  271     if ((end <= (char *) packet->payload + packet->payload_size)
  272         && (begin >= (char *) packet->payload))
  273         return true;
  274     else
  275         return false;
  276 }
  277 
  278 static void SDFSearchRecursively(SDFConfig *config, SFSnortPacket *packet,
  279                       SDFSessionData *session, sdf_tree_node *matched_node, 
  280                       char **position, uint16_t *buflen, uint16_t match_length, bool *ob_failed)
  281 {
  282     /* Iterate through the SDFOptionData that matches this pattern. */
  283     uint16_t i;
  284     for (i = 0; i < matched_node->num_option_data; i++)
  285     {
  286         SDFOptionData *found_pattern = matched_node->option_data_list[i];
  287         if (found_pattern->match_success)
  288         {
  289             int index;
  290             /* Reset the match_success flag for subsequent matches */
  291             found_pattern->match_success = 0;
  292 
  293             /* Check the RTN for the PII we found. The IPs & ports might not match.
  294                We only want to do this once per session */
  295             index = found_pattern->counter_index;
  296             if (session->rtns_matched[index] == 0)
  297             {
  298                 bool check_ports = true;
  299                 OptTreeNode *otn = found_pattern->otn;
  300                 RuleTreeNode *rtn = NULL;
  301 #ifdef TARGET_BASED
  302                 int16_t app_ordinal;
  303 #endif
  304 
  305                 if (_dpd.getIpsRuntimePolicy() < otn->proto_node_num)
  306                     rtn = otn->proto_nodes[_dpd.getIpsRuntimePolicy()];
  307 
  308 #ifdef TARGET_BASED
  309                 /* Check the service against the matched OTN. */
  310                 app_ordinal = _dpd.sessionAPI->get_application_protocol_id(packet->stream_session);
  311                 if( app_ordinal != SFTARGET_UNKNOWN_PROTOCOL )
  312                 {
  313                     unsigned int i;
  314                     for (i = 0; i < otn->sigInfo.num_services; i++)
  315                     {
  316                         if (otn->sigInfo.services[i].service_ordinal == app_ordinal)
  317                         {
  318                             check_ports = false;
  319                             break;
  320                         }
  321                     }
  322                 }
  323 #endif
  324                 if (rtn != NULL && _dpd.fpEvalRTN(rtn, packet, check_ports))
  325                     session->rtns_matched[index] = 1;
  326                 else
  327                     session->rtns_matched[index] = -1;
  328             }
  329 
  330             if (session->rtns_matched[index] == 1)
  331             {
  332                 /* Increment counters */
  333                 session->counters[found_pattern->counter_index]++;
  334 
  335                 /* Obfuscate the data.
  336                    We do this even if it's not time to alert, to obfuscate each match.
  337                    Only obfuscate built-in patterns */
  338                 if (config->mask_output && found_pattern->validate_func)
  339                 {
  340                     if (isBufferInPayload(*position, *position + match_length, packet))
  341                     {
  342                         uint16_t offset, ob_length = 0;
  343 
  344                         offset = (uint16_t) ((*position) - (char *)packet->payload);
  345 
  346                         if (match_length > SDF_OBFUSCATION_DIGITS_SHOWN)
  347                             ob_length = match_length - SDF_OBFUSCATION_DIGITS_SHOWN;
  348 
  349                         /* Generally, the CC# and SS# patterns contain non-digits on either
  350                          * side of the actual number. Sometimes, for the patterns from the
  351                          * first line of the data might start  with a digit, instead of a
  352                          * non-digit. Adjust the mask to match.
  353                          */
  354                         if (isdigit((int)*position[0]))
  355                             ob_length = ob_length - 1;
  356 
  357                         else
  358                         {
  359                             offset = offset + 1;
  360                             ob_length = ob_length - 2;
  361                         }
  362                         
  363                         _dpd.obApi->addObfuscationEntry(packet, offset, ob_length,
  364                                                         SDF_OBFUSCATION_CHAR);
  365                     }
  366                     else
  367                     {
  368                         *ob_failed = true;
  369                     }
  370                 }
  371 
  372                 if (session->counters[found_pattern->counter_index] == found_pattern->count)
  373                 {
  374 
  375                     /* Raise the alert for this particular pattern */
  376                     _dpd.alertAdd(GENERATOR_SPP_SDF_RULES,
  377                                   found_pattern->otn->sigInfo.id,
  378                                   found_pattern->otn->sigInfo.rev,
  379                                   found_pattern->otn->sigInfo.class_id,
  380                                   found_pattern->otn->sigInfo.priority,
  381                                   found_pattern->otn->sigInfo.message,
  382                                   0);
  383                 }
  384             }
  385         }
  386     }
  387 
  388     /* Check the global counter and alert */
  389     session->global_counter++;
  390     if (session->global_counter == config->threshold)
  391     {
  392         /* Do our "combo alert" */
  393         SDFPrintPseudoPacket(config, session, packet);
  394         _dpd.genSnortEvent(config->pseudo_packet,
  395                            GENERATOR_SPP_SDF_PREPROC,
  396                            SDF_COMBO_ALERT_SID,
  397                            SDF_COMBO_ALERT_REV,
  398                            SDF_COMBO_ALERT_CLASS,
  399                            SDF_COMBO_ALERT_PRIORITY,
  400                            SDF_COMBO_ALERT_STR);
  401     }
  402 
  403     /* Update position */
  404     if (match_length > 1)
  405     {
  406         (*position) += match_length - 1;
  407         (*buflen) -= match_length - 1;
  408     }
  409 }
  410 
  411 /* Search a buffer for PII. Generates alerts when enough PII is found.
  412    Returns: void
  413 */
  414 static void SDFSearch(SDFConfig *config, SFSnortPacket *packet,
  415                       SDFSessionData *session, char *position, char *end,
  416                       uint16_t buflen, bool *ob_failed)
  417 {
  418     uint16_t match_length = 0;
  419     sdf_tree_node *matched_node = NULL;
  420     uint16_t *partial_index = &(session->part_match_index);
  421     sdf_tree_node **partial_node = &(session->part_match_node);
  422     /* Check to see if there was a partial match */ 
  423     if(*partial_index > 0)
  424     {
  425         if( position < end )
  426         {
  427             sdf_tree_node *node = *partial_node;
  428             if(strlen(node->pattern) == *partial_index)
  429             {
  430                 int i = 0;
  431                 while ((i < node->num_children) && matched_node == NULL)
  432                 {
  433                     *partial_index = 0;
  434                     matched_node = FindPiiRecursively(node->children[i], position, &match_length, buflen, config, partial_index, partial_node);
  435                     i++;
  436                 }
  437             }
  438             else
  439             {
  440                 matched_node = FindPiiRecursively(node, position, &match_length, buflen, config, partial_index, partial_node);
  441             }
  442 
  443             /* only when matched update the position ptr. FindPiiRecursively only checks one node unlike FindPii */
  444             if (matched_node)
  445                 SDFSearchRecursively(config, packet, session, matched_node, &position, &buflen, match_length, ob_failed);
  446             else if (*partial_index)
  447             {
  448                 position += match_length;
  449                 buflen -= match_length;
  450             }
  451         }
  452         else
  453         {
  454             return;
  455         }
  456     }
  457 
  458     while (position < end)
  459     {
  460         match_length = 0;
  461         matched_node = NULL;
  462 
  463         /* Traverse the pattern tree and match PII against our data */
  464         matched_node = FindPii(sdf_context->head_node, position, &match_length,
  465                                buflen, config, session);
  466 
  467         if (matched_node)
  468             SDFSearchRecursively(config, packet, session, matched_node, &position, &buflen, match_length, ob_failed);
  469         else if (*partial_index)
  470         {
  471             position += match_length;
  472             buflen -= match_length;
  473         }
  474         else
  475         {
  476             position++;
  477             buflen--;
  478         }
  479     }
  480 }
  481 
  482 /*
  483  * Function: ProcessSDF(void *, void *)
  484  *
  485  * Purpose: Inspects a packet's payload for Personally Identifiable Information
  486  *
  487  * Arguments: p => poitner to the current packet data struct
  488  *            context => unused void pointer
  489  *
  490  * Returns: void
  491  *
  492  */
  493 static void ProcessSDF(void *p, void *context)
  494 {
  495     tSfPolicyId policy_id;
  496     SDFConfig *config = NULL;
  497     SFSnortPacket *packet = (SFSnortPacket *)p;
  498     SDFSessionData *session;
  499     char *begin, *end;
  500     uint16_t buflen;
  501     bool payload_checked = false;
  502     bool ob_failed = false;
  503 
  504     PROFILE_VARS;
  505 
  506     // preconditions - what we registered for
  507     assert((IsUDP(packet) || IsTCP(packet)) &&
  508         packet->payload && packet->payload_size);
  509 
  510     /* Check if we should be working on this packet */
  511     if ( packet->flags & FLAG_STREAM_INSERT && (!(packet->flags & FLAG_PDU_TAIL)) )
  512         return;  // Waiting on stream reassembly
  513 
  514     /* Retrieve the corresponding config for this packet */
  515     policy_id = _dpd.getIpsRuntimePolicy();
  516     sfPolicyUserPolicySet (sdf_context->context_id, policy_id);
  517     config = sfPolicyUserDataGetCurrent(sdf_context->context_id);
  518 
  519     /* Retrieve stream session data. Create one if it doesn't exist. */
  520     session = _dpd.sessionAPI->get_application_data(packet->stream_session, PP_SDF);
  521     if (session == NULL)
  522     {
  523         char pseudo_start[1] = {'0'};
  524         /* Do port checks */
  525         if (SDFCheckPorts(config, packet) == 0)
  526         {
  527             return;
  528         }
  529 
  530         /* If there's no stream session, we'll just count PII for one packet */
  531         if (packet->stream_session == NULL)
  532         {
  533             if (config->stateless_session == NULL)
  534                 config->stateless_session = NewSDFSession(config, packet);
  535 
  536             session = config->stateless_session;
  537             memset(session->counters, 0, session->num_patterns);
  538             memset(session->rtns_matched, 0, session->num_patterns);
  539         }
  540         else
  541             session = NewSDFSession(config, packet);
  542 
  543         /* Add one byte to support sensitive data starts with first byte */
  544         begin = pseudo_start;
  545         buflen = 1;
  546         end = begin + buflen;
  547         SDFSearch(config, packet, session, begin, end, buflen, &ob_failed);
  548 
  549     }
  550     else if( session->config_num != config->config_num )
  551     {
  552         /* Config has changed. Don't use rule tree nodes from previous config */
  553         session->part_match_index = 0;
  554         session->part_match_node = NULL;
  555         /* Update the session's config num */
  556         session->config_num = config->config_num;
  557     }
  558 
  559     PREPROC_PROFILE_START(sdfPerfStats);
  560 
  561     /* By First checking ports and protocols for a given packet , Snort is able to limit the number of rules that must be evaluated.
  562        prmFindRuleGroup()  will be called to make sure a match exists for the source and destination ports mentioned in the rule. 
  563        Sometimes more than one rule group will be matched to the same packet. This is quick way to eliminate the rules without complex pattern matching. 
  564        Sometimes same packet will be evaluated by Sensitive Data Preprocessor  more than once because packet is matched to more than one rule group.  
  565        When evaluating same packet for each rule group by SDF,  PII count  will be incremented for the same packet which causes mis-firing alert. 
  566        Need to avoid evaluating same packet multiple times by SDF.
  567 
  568     */
  569     if(packet->tcp_header)
  570     {
  571          if( (session->last_pkt_seq_num == packet->tcp_header->sequence) )
  572          {
  573               if( (_dpd.fileDataBuf->len == 0 ) ||
  574                   (session->last_pkt_data_len != _dpd.fileDataBuf->len ))
  575               {
  576                     // Process the same packet if file data buffer len is diffrent or zero. 
  577                     session->last_pkt_data_len = _dpd.fileDataBuf->len;
  578               }
  579               else
  580               {
  581                    // Do not evaluate the same packet if file data buffer len is same.
  582                    session->last_pkt_data_len = _dpd.fileDataBuf->len;
  583                    return;
  584               }
  585          }
  586          else
  587          {
  588               session->last_pkt_seq_num = packet->tcp_header->sequence;
  589               session->last_pkt_data_len = _dpd.fileDataBuf->len;
  590          }
  591     } 
  592 
  593     /* Inspect HTTP Body or Email attachments. */
  594     if (_dpd.fileDataBuf->len > 0)
  595     {
  596         begin = (char *) _dpd.fileDataBuf->data;
  597         buflen = _dpd.fileDataBuf->len;
  598         end = begin + buflen;
  599 
  600         SDFSearch(config, packet, session, begin, end, buflen, &ob_failed);
  601     }
  602     else if ( PacketHasPAFPayload(packet) )
  603     {
  604         /* SDF already requires stream to be enabled, might as well look
  605          * at the rebuilt packet */
  606 
  607         begin = (char *)packet->payload;
  608         buflen = packet->payload_size;
  609         end = begin + buflen;
  610 
  611         SDFSearch(config, packet, session, begin, end, buflen, &ob_failed);
  612         payload_checked = true;
  613     }
  614 
  615     /* If this packet is HTTP, inspect the URI and Client Body while ignoring
  616      * headers. */
  617     if (packet->flags & FLAG_HTTP_DECODE)
  618     {
  619         unsigned len;
  620         begin = (char*)_dpd.getHttpBuffer(HTTP_BUFFER_URI, &len);
  621 
  622         if ( begin )
  623         {
  624             buflen = (uint16_t)len;
  625             end = begin + buflen;
  626             if (!payload_checked || !isBufferInPayload(begin, end, packet))
  627                 SDFSearch(config, packet, session, begin, end, buflen, &ob_failed);
  628         }
  629         begin = (char*)_dpd.getHttpBuffer(HTTP_BUFFER_CLIENT_BODY, &len);
  630 
  631         if ( begin )
  632         {
  633             buflen = (uint16_t)len;
  634             end = begin + buflen;
  635             if (!payload_checked || !isBufferInPayload(begin, end, packet))
  636                 SDFSearch(config, packet, session, begin, end, buflen, &ob_failed);
  637         }
  638     }
  639 
  640     /* Found match but not in payload, recheck to mask the rebuilt packet */
  641     if ( !payload_checked && ob_failed && PacketHasPAFPayload(packet) )
  642     {
  643         begin = (char *)packet->payload;
  644         buflen = packet->payload_size;
  645         end = begin + buflen;
  646 
  647         SDFSearch(config, packet, session, begin, end, buflen, &ob_failed);
  648         payload_checked = true;
  649     }
  650 
  651     /* End. */
  652     PREPROC_PROFILE_END(sdfPerfStats);
  653     return;
  654 }
  655 
  656 static void DisplaySDFConfig(SDFConfig *config)
  657 {
  658     if (config == NULL)
  659         return;
  660 
  661     _dpd.logMsg("Sensitive Data preprocessor config: \n");
  662     _dpd.logMsg("    Global Alert Threshold: %d\n", config->threshold);
  663     _dpd.logMsg("    Masked Output: %s\n",
  664             config->mask_output ? "ENABLED" : "DISABLED" );
  665 }
  666 
  667 /*
  668  * Function: ParseSDFArgs(SDFConfig *, char *)
  669  *
  670  * Purpose: Parse the arguments to the SDF preprocessor and instantiate a
  671  *          SDFConfig struct.
  672  *
  673  * Arguments: config => pointer to a newly-allocated SDFConfig struct, which
  674  *                      will be modified.
  675  *            args => pointer to string containing SDF preproc arguments.
  676  *
  677  * Returns: void
  678  *
  679  */
  680 static void ParseSDFArgs(SDFConfig *config, char *args)
  681 {
  682     char *argcpy = NULL;
  683     char *cur_tokenp = NULL;
  684 
  685     if (config == NULL || args == NULL) return;
  686 
  687     /* Set default options */
  688     SSNSetDefaultGroups(config);
  689 
  690     /* Copy args so that we can break them up wtih strtok */
  691     argcpy = strdup(args);
  692     if (argcpy == NULL)
  693         DynamicPreprocessorFatalMessage("Could not allocate memory to parse "
  694                                         "SDF options.\n");
  695 
  696     cur_tokenp = strtok(argcpy, " ");
  697 
  698     /* Loop through config options */
  699     while (cur_tokenp)
  700     {
  701         /* Parse the global PII threshold */
  702         if (!strcmp(cur_tokenp, SDF_THRESHOLD_KEYWORD))
  703         {
  704             char *endptr;
  705 
  706             cur_tokenp = strtok(NULL, " ");
  707             if (cur_tokenp == NULL)
  708             {
  709                 DynamicPreprocessorFatalMessage("SDF preprocessor config option "
  710                         "\"%s\" requires an argument.\n", SDF_THRESHOLD_KEYWORD);
  711             }
  712 
  713             if (*cur_tokenp == '-')
  714             {
  715                 DynamicPreprocessorFatalMessage("SDF preprocessor config option "
  716                         "\"%s\" cannot take a negative argument.\n",
  717                         SDF_THRESHOLD_KEYWORD);
  718             }
  719 
  720             config->threshold = _dpd.SnortStrtoul(cur_tokenp, &endptr, 10);
  721             if (config->threshold == 0 || config->threshold > USHRT_MAX)
  722             {
  723                 DynamicPreprocessorFatalMessage("SDF preprocessor config option "
  724                         "\"%s\" must have an argument between 1 - %u.\n",
  725                         SDF_THRESHOLD_KEYWORD, USHRT_MAX);
  726             }
  727             if (*endptr != '\0')
  728             {
  729                 DynamicPreprocessorFatalMessage("Invalid argument to SDF config "
  730                         "option \"%s\": %s", SDF_THRESHOLD_KEYWORD, cur_tokenp);
  731             }
  732         }
  733         /* Parse the output masking option */
  734         else if (!strcmp(cur_tokenp, SDF_MASK_KEYWORD))
  735         {
  736             config->mask_output = 1;
  737         }
  738         /* Parse the file containing new SSN group data */
  739         else if (!strcmp(cur_tokenp, SDF_SSN_FILE_KEYWORD))
  740         {
  741             int iRet;
  742 
  743             cur_tokenp = strtok(NULL, " ");
  744             if (cur_tokenp == NULL)
  745             {
  746                 DynamicPreprocessorFatalMessage("SDF preprocessor config option "
  747                         "\"%s\" requires an argument.\n", SDF_SSN_FILE_KEYWORD);
  748             }
  749 
  750             iRet = ParseSSNGroups(cur_tokenp, config);
  751             if (iRet < 0)
  752             {
  753                 DynamicPreprocessorFatalMessage("Error parsing Social Security "
  754                         "group data from file: %s", cur_tokenp);
  755             }
  756         }
  757 
  758         else
  759         {
  760                 DynamicPreprocessorFatalMessage("%s(%d) => Unknown SDF configuration option %s\n",
  761                                             *(_dpd.config_file), *(_dpd.config_line), cur_tokenp);
  762         }
  763 
  764         cur_tokenp = strtok(NULL, " ");
  765     }
  766 
  767     /* Cleanup */
  768     DisplaySDFConfig(config);
  769     free(argcpy);
  770     argcpy = NULL;
  771 }
  772 
  773 /* Allocate & Initialize the pseudo-packet used for logging combo alerts.
  774  *
  775  * Returns: 0 on success, -1 on error.
  776  */
  777 static int SDFPacketInit(SDFConfig *config)
  778 {
  779     config->pseudo_packet = _dpd.encodeNew();
  780     return 0;
  781 }
  782 
  783 /*
  784  * Function: NewSDFConfig(void)
  785  *
  786  * Purpose: Create a new SDFConfig for the current parser policy.
  787  *
  788  * Arguments: context => context ID to use when creating config
  789  *
  790  * Returns: Pointer to newly created SDFConfig struct.
  791  *
  792  */
  793 static SDFConfig * NewSDFConfig(struct _SnortConfig *sc, tSfPolicyUserContextId context)
  794 {
  795     SDFConfig *config = NULL;
  796     tSfPolicyId policy_id = _dpd.getParserPolicy(sc);
  797 
  798     /* Check for an existing configuration in this policy */
  799     sfPolicyUserPolicySet(context, policy_id);
  800 
  801     config = (SDFConfig *) sfPolicyUserDataGetCurrent(context);
  802     if (config)
  803         DynamicPreprocessorFatalMessage("SDF preprocessor can only be "
  804                                         "configured once.\n");
  805 
  806     /* Create and store config */
  807     config = (SDFConfig *)calloc(1, sizeof(SDFConfig));
  808     if (!config)
  809         DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF "
  810                                         "configuration.\n");
  811     sfPolicyUserDataSetCurrent(context, config);
  812 
  813     /* Allocate the pseudo-packet used for logging */
  814     SDFPacketInit(config);
  815     config->config_num = sdf_config_count++;
  816 
  817     return config;
  818 }
  819 
  820 /*
  821  * Function: SDFCleanExit(int, void *)
  822  *
  823  * Purpose: Free memory used by the SDF preprocessor before Snort exits.
  824  *
  825  * Arguments: Signal sent to Snort, unused void pointer
  826  *
  827  * Returns: void
  828  *
  829  */
  830 static void SDFCleanExit(int signal, void *unused)
  831 {
  832     /* Free the individual configs. */
  833     if (sdf_context == NULL)
  834         return;
  835 
  836     sfPolicyUserDataFreeIterate(sdf_context->context_id, SDFFreeConfig);
  837     sfPolicyConfigDelete(sdf_context->context_id);
  838     FreePiiTree(sdf_context->head_node);
  839     free(sdf_context);
  840     sdf_context = NULL;
  841 }
  842 
  843 /*
  844  * Function: SDFFreeConfig(tSfPolicyUserContextId, tSfPolicyId, void *)
  845  *
  846  * Purpose: Callback that frees a SDFConfig struct correctly, and clears data
  847  *          from the policy.
  848  *
  849  * Arguments: context => context ID for the SDF preprocessor
  850  *            id => policy ID for the policy being destroyed
  851  *            pData => pointer to SDFConfig struct that gets freed
  852  *
  853  * Returns: zero
  854  *
  855  */
  856 static int SDFFreeConfig(tSfPolicyUserContextId context, tSfPolicyId id, void *pData)
  857 {
  858     SDFConfig *config = (SDFConfig *)pData;
  859 
  860     sfPolicyUserDataClear(context, id);
  861 
  862     _dpd.encodeDelete(config->pseudo_packet);
  863     FreeSDFSession(config->stateless_session);
  864 
  865     free(config);
  866     return 0;
  867 }
  868 
  869 #ifdef SNORT_RELOAD
  870 static void SDFReload(struct _SnortConfig *sc, char *args, void **new_config)
  871 {
  872     SDFContext *sdf_swap_context = (SDFContext *)*new_config;
  873     SDFConfig *config = NULL;
  874 
  875     if (sdf_swap_context == NULL)
  876     {
  877         if (!_dpd.streamAPI)
  878             DynamicPreprocessorFatalMessage("SetupSDF(): The Stream preprocessor "
  879                                             "must be enabled.\n");
  880 
  881         sdf_swap_context = (SDFContext *)calloc(1, sizeof(*sdf_context));
  882         if (!sdf_swap_context)
  883             DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF "
  884                                             "configuration.\n");
  885         sdf_swap_context->context_id = sfPolicyConfigCreate();
  886         if (!sdf_swap_context->context_id)
  887             DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF "
  888                                             "configuration.\n");
  889         sdf_swap_context->head_node = (sdf_tree_node *)calloc(1, sizeof(*sdf_swap_context->head_node));
  890         if (!sdf_swap_context->head_node)
  891             DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF "
  892                                             "configuration.\n");
  893         *new_config = (void *)sdf_swap_context;
  894     }
  895 
  896     config = NewSDFConfig(sc, sdf_swap_context->context_id);
  897     ParseSDFArgs(config, args);
  898 
  899     _dpd.addDetect(sc, ProcessSDF, PRIORITY_FIRST, PP_SDF,
  900             PROTO_BIT__TCP | PROTO_BIT__UDP);
  901     _dpd.preprocOptRegister(sc, SDF_OPTION_NAME, SDFOptionInit, SDFOptionEval,
  902                                 NULL, NULL, NULL, SDFOtnHandler, NULL);
  903 }
  904 
  905 static void * SDFReloadSwap(struct _SnortConfig *sc, void *swap_config)
  906 {
  907     SDFContext *sdf_swap_context = (SDFContext *)swap_config;
  908     SDFContext *old_context = sdf_context;
  909 
  910     if (old_context == NULL || sdf_swap_context == NULL)
  911         return NULL;
  912 
  913     sdf_context = sdf_swap_context;
  914 
  915     return (void *) old_context;
  916 }
  917 
  918 static void SDFReloadSwapFree(void *data)
  919 {
  920     SDFContext *context = (SDFContext *) data;
  921     if (context == NULL)
  922         return;
  923 
  924     sfPolicyUserDataFreeIterate(context->context_id, SDFFreeConfig);
  925     sfPolicyConfigDelete(context->context_id);
  926     FreePiiTree(context->head_node);
  927     free(context);
  928 }
  929 #endif
  930 
  931 static void SDFPrintPseudoPacket(SDFConfig *config, SDFSessionData *session,
  932                                  SFSnortPacket *real_packet)
  933 {
  934     SFSnortPacket* p;
  935 
  936     if (config == NULL || session == NULL || real_packet == NULL)
  937         return;
  938 
  939     p = config->pseudo_packet;
  940 
  941     _dpd.encodeFormat(ENC_DYN_FWD|ENC_DYN_NET, real_packet, config->pseudo_packet, PSEUDO_PKT_SDF);
  942 
  943     if ( IS_IP4(real_packet) )
  944     {
  945         ((IPV4Header *)p->ip4_header)->proto = IPPROTO_SDF;
  946         p->inner_ip4h.ip_proto = IPPROTO_SDF;
  947     }
  948     else if (IS_IP6(p))
  949     {
  950         // FIXTHIS assumes there are no ip6 extension headers
  951         p->inner_ip6h.next = IPPROTO_SDF;
  952         p->ip6h = &p->inner_ip6h;
  953     }
  954 
  955     /* Fill in the payload with SDF alert info */
  956     SDFFillPacket(sdf_context->head_node, session, p, &p->payload_size);
  957 
  958     _dpd.encodeUpdate(config->pseudo_packet);
  959 
  960     if (real_packet->family == AF_INET)
  961     {
  962         p->ip4h->ip_len = p->ip4_header->data_length;
  963     }
  964     else
  965     {
  966         IP6RawHdr* ip6h = (IP6RawHdr*)p->raw_ip6_header;
  967         if ( ip6h ) p->ip6h->len = ip6h->ip6_payload_len;
  968     }
  969 }
  970 
  971 /* This function traverses the pattern tree and prints out the relevant
  972  * info into a provided pseudo-packet. */
  973 static void SDFFillPacket(sdf_tree_node *node, SDFSessionData *session,
  974                           SFSnortPacket *p, uint16_t *dlen)
  975 {
  976     uint16_t i;
  977 
  978     if (node == NULL || session == NULL || p == NULL || dlen == NULL)
  979         return;
  980 
  981     /* Recurse to the leaves of the pattern tree */
  982     for (i = 0; i < node->num_children; i++)
  983     {
  984         SDFFillPacket(node->children[i], session, p, dlen);
  985     }
  986 
  987     for (i = 0; i < node->num_option_data; i++)
  988     {
  989         SDFOptionData * option_data = node->option_data_list[i];
  990 
  991         /* Print the info from leaves */
  992         if (option_data)
  993         {
  994             uint32_t index = option_data->counter_index;
  995             uint8_t counter = session->counters[index];
  996             if (counter > 0)
  997             {
  998                 /* Print line */
  999                 const char *sigmessage = option_data->otn->sigInfo.message;
 1000                 uint8_t *dest = (uint8_t*)p->payload + *dlen;
 1001                 size_t siglen = strlen(sigmessage);
 1002                 uint16_t space_left = p->max_payload - *dlen;
 1003 
 1004                 if (space_left < siglen + SDF_ALERT_LENGTH)
 1005                     return;
 1006 
 1007                 *dlen += (siglen + SDF_ALERT_LENGTH);
 1008                 snprintf((char *)dest, space_left, "%s: %3d", sigmessage, counter);
 1009             }
 1010         }
 1011     }
 1012 
 1013     return;
 1014 }
 1015 
 1016