1 /*
2 ** Copyright (C) 2014-2021 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  */
SetupSDF(void)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  */
SDFInit(struct _SnortConfig * sc,char * args)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  */
SDFCheckPorts(SDFConfig * config,SFSnortPacket * packet)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 */
FreeSDFSession(void * 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 */
NewSDFSession(SDFConfig * config,SFSnortPacket * packet)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 
isBufferInPayload(char * begin,char * end,SFSnortPacket * packet)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 
SDFSearchRecursively(SDFConfig * config,SFSnortPacket * packet,SDFSessionData * session,sdf_tree_node * matched_node,char ** position,uint16_t * buflen,uint16_t match_length,bool * ob_failed)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 */
SDFSearch(SDFConfig * config,SFSnortPacket * packet,SDFSessionData * session,char * position,char * end,uint16_t buflen,bool * ob_failed)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  */
ProcessSDF(void * p,void * context)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 
DisplaySDFConfig(SDFConfig * config)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  */
ParseSDFArgs(SDFConfig * config,char * args)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  */
SDFPacketInit(SDFConfig * config)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  */
NewSDFConfig(struct _SnortConfig * sc,tSfPolicyUserContextId context)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  */
SDFCleanExit(int signal,void * unused)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  */
SDFFreeConfig(tSfPolicyUserContextId context,tSfPolicyId id,void * pData)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
SDFReload(struct _SnortConfig * sc,char * args,void ** new_config)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 
SDFReloadSwap(struct _SnortConfig * sc,void * swap_config)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 
SDFReloadSwapFree(void * data)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 
SDFPrintPseudoPacket(SDFConfig * config,SDFSessionData * session,SFSnortPacket * real_packet)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. */
SDFFillPacket(sdf_tree_node * node,SDFSessionData * session,SFSnortPacket * p,uint16_t * dlen)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 
1017