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